/*
    Copyright (C) 2005-2007  Michel de Boer <michel@twinklephone.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <assert.h>
#include <iostream>
#include "log.h"
#include "events.h"
#include "timekeeper.h"
#include "transaction.h"
#include "transaction_mgr.h"
#include "user.h"
#include "util.h"
#include "audits/memman.h"

extern t_event_queue		*evq_sender_udp;
extern t_event_queue		*evq_trans_layer;
extern t_transaction_mgr	*transaction_mgr;

string trans_state2str(t_trans_state s) {
	switch(s) {
	case TS_NULL:		return "TS_NULL";
	case TS_CALLING:	return "TS_CALLING";
	case TS_TRYING:		return "TS_TRYING";
	case TS_PROCEEDING:	return "TS_PROCEEDING";
	case TS_COMPLETED:	return "TS_COMPLETED";
	case TS_CONFIRMED:	return "TS_CONFIRMED";
	case TS_TERMINATED:	return "TS_TERMINATED";
	}

	return "UNKNOWN";
}

///////////////////////////////////////////////////////////
// RFC 3261 17
// General transaction
///////////////////////////////////////////////////////////

t_mutex t_transaction::mtx_class;
t_tid t_transaction::next_id = 1;

t_transaction::t_transaction(t_request *r, unsigned short _tuid) {
	mtx_class.lock();
	id = next_id++;
	if (next_id == 65535) next_id = 1;
	mtx_class.unlock();
	
	state = TS_NULL;
	request = (t_request *)r->copy();
	final = NULL;
	tuid = _tuid;
}

t_transaction::~t_transaction() {
	MEMMAN_DELETE(request);
	delete request;
	if (final != NULL) {
		MEMMAN_DELETE(final);
		delete final;
	}

	for (list<t_response *>::iterator i = provisional.begin();
	     i != provisional.end(); i++)
	{
		MEMMAN_DELETE(*i);
		delete *i;
	}
}

t_tid t_transaction::get_id(void) const {
	return id;
}

void t_transaction::process_provisional(t_response *r) {
	provisional.push_back((t_response *)r->copy());
}

void t_transaction::process_final(t_response *r) {
	final = (t_response *)r->copy();
}

void t_transaction::process_response(t_response *r) {
	if (r->is_provisional()) {
		process_provisional(r);
	} else {
		process_final(r);
	}
}

t_trans_state t_transaction::get_state(void) const {
	return state;
}

void t_transaction::set_tuid(unsigned short _tuid) {
	tuid = _tuid;
}

t_method t_transaction::get_method(void) const {
	return request->method;
}

string t_transaction::get_to_tag(void) {
	string	tag;

	tag = request->hdr_to.tag;
	if (tag.size() > 0) return tag;
	if (to_tag.size() > 0) return to_tag;
	to_tag = random_token(TAG_LEN);
	return to_tag;
}

// RCF 3261 section 8.2.6.2
t_response *t_transaction::create_response(int code, string reason) {
	t_response *r;

	r = request->create_response(code, reason);
	
	// NOTE: 100 Trying does not establish a dialog
	if (code != R_100_TRYING) {
		r->hdr_to.set_tag(get_to_tag());
	}

	return r;
}

///////////////////////////////////////////////////////////
// RFC 3261 17.1
// Client transaction
///////////////////////////////////////////////////////////

t_trans_client::t_trans_client(t_request *r, unsigned long ipaddr,
		unsigned short port, unsigned short _tuid) :
	t_transaction(r, _tuid)
{
	dst_ipaddr = ipaddr;
	dst_port = port;

	// Send request
	evq_sender_udp->push_network(r, ipaddr, port);
}

// RFC 3261 17.1.3, 8.2.6.2
// Section 17.1.3 states that only the branch and CSeq method should match.
// This can lead to the following problem however:
//
// 1) A response matches a BYE request, but has a wrong call id.
// 2) As the response matches the request, the transaction finishes.
// 3) Then the response is delivered to the TU which tries to match the
//    response to a dialog.
// 4) As the call id is wrong, no match is found an the response is discarded.
// 5) Now the TU keeps waiting forever for a response on the BYE
//
// By taking the call id into account here, this scenario is prevented.
// When a call id is wrong, the BYE request will be retransmitted due to
// timeouts until the transaction times out completely and a 408 is sent
// to the TU.
//
// Same problem can occur when tags do not match, so tag is take into account
// as well. So tags are take into account as well.
bool t_trans_client::match(t_response *r) const {
	t_via	&req_top_via = request->hdr_via.via_list.front();
	t_via	&resp_top_via = r->hdr_via.via_list.front();

	return (req_top_via.branch == resp_top_via.branch &&
		request->hdr_cseq.method == r->hdr_cseq.method &&
		request->hdr_call_id.call_id == r->hdr_call_id.call_id &&
		request->hdr_from.tag == r->hdr_from.tag &&
		(request->hdr_to.tag.empty() || request->hdr_to.tag == r->hdr_to.tag));
}

// An ICMP error matches a transaction when the destination IP address/port
// of the packet that caused the ICMP error equals the destination 
// IP address/port of the transaction. Other information of the packet causing
// the ICMP error is not available.
// In theory when multiple transactions are open for the same destination, the
// wrong transaction may process the ICMP error. In practice this should rarely
// happen as the destination will be unreachable for all those transactions.
// If it happens a transaction gets aborted.
bool t_trans_client::match(const t_icmp_msg &icmp) const {
	return (dst_ipaddr == icmp.ipaddr && dst_port == icmp.port);
}

void t_trans_client::process_provisional(t_response *r) {
	// Set the to_tag, such that an internally genrated answer (when needed) 
	// will have the correct tag.
	// An INVITE transaction may receive provisional responses with
	// different to-tags. Only the first to-tag will be kept and an
	// internally generated response will match this tag.
	if (!r->hdr_to.tag.empty() && to_tag.empty()) {
		to_tag = r->hdr_to.tag;
	}
	
	t_transaction::process_provisional(r);
}

///////////////////////////////////////////////////////////
// RFC 3261 17.1.1
// Client INVITE transaction
///////////////////////////////////////////////////////////

void t_tc_invite::start_timer_A(void) {
	timer_A = transaction_mgr->start_timer(duration_A, TIMER_A, id);

	// Double duration for a next start
	duration_A = 2 * duration_A;
}

void t_tc_invite::start_timer_B(void) {
	timer_B = transaction_mgr->start_timer(DURATION_B, TIMER_B, id);
}

void t_tc_invite::start_timer_D(void) {
	timer_D = transaction_mgr->start_timer(DURATION_D, TIMER_D, id);
}

void t_tc_invite::stop_timer_A(void) {
	if (timer_A) {
		transaction_mgr->stop_timer(timer_A);
		timer_A = 0;
	}
}

void t_tc_invite::stop_timer_B(void) {
	if (timer_B) {
		transaction_mgr->stop_timer(timer_B);
		timer_B = 0;
	}
}

void t_tc_invite::stop_timer_D(void) {
	if (timer_D) {
		transaction_mgr->stop_timer(timer_D);
		timer_D = 0;
	}
}

t_tc_invite::t_tc_invite(t_request *r, unsigned long ipaddr,
		unsigned short port, unsigned short _tuid) :
	t_trans_client(r, ipaddr, port, _tuid)
{
	assert(r->method == INVITE);

	ack = NULL;
	duration_A = DURATION_A;
	state = TS_CALLING;

	start_timer_A();
	start_timer_B();
	timer_D = 0;
}

t_tc_invite::~t_tc_invite() {
	if (ack != NULL) {
		MEMMAN_DELETE(ack);
		delete ack;
	}
	stop_timer_A();
	stop_timer_B();
	stop_timer_D();
}

void t_tc_invite::process_provisional(t_response *r) {
	assert(r->is_provisional());

	switch (state) {
	case TS_CALLING:
		stop_timer_A();
		stop_timer_B();
		// fall through
	case TS_PROCEEDING:
		t_trans_client::process_provisional(r);
		state = TS_PROCEEDING;

		// Report to TU
		evq_trans_layer->push_user(r, tuid, id);
		break;
	default:
		// Discard provisional response in other states
		break;
	}
}

void t_tc_invite::process_final(t_response *r) {
	assert(r->is_final());
	
	unsigned long ipaddr;
	unsigned short port;

	switch (state) {
	case TS_CALLING:
		stop_timer_A();
		stop_timer_B();
		// fall through
	case TS_PROCEEDING:
		t_trans_client::process_final(r);

		if (r->is_success()) {
			state = TS_TERMINATED;
		} else {
			// RFC 3261 17.1.1.3
			// construct ACK
			ack = new t_request(ACK);
			MEMMAN_NEW(ack);
			ack->uri = request->uri;
			ack->hdr_call_id = request->hdr_call_id;
			ack->hdr_from = request->hdr_from;
			ack->hdr_to = r->hdr_to;
			ack->hdr_via.add_via(
				request->hdr_via.via_list.front());
			ack->hdr_cseq.set_seqnr(request->hdr_cseq.seqnr);
			ack->hdr_cseq.set_method(ACK);
			ack->hdr_route = request->hdr_route;
			ack->hdr_max_forwards.set_max_forwards(MAX_FORWARDS);
			SET_HDR_USER_AGENT(ack->hdr_user_agent)

			// RFC 3261 22.1
			// Duplicate Authorization and Proxy-Authorization
			// headers from INVITE if the credentials in the
			// INVITE are accepted.
			if (r->code != R_401_UNAUTHORIZED &&
			    r->code != R_407_PROXY_AUTH_REQUIRED)
			{
				ack->hdr_authorization =
					request->hdr_authorization;
				ack->hdr_proxy_authorization =
					request->hdr_proxy_authorization;
			}
			
			// RFC 3263 4
			// ACK for non-2xx SIP responses to INVITE MUST be sent t
			// to the same host.
			request->get_current_destination(ipaddr, port);
			ack->set_destination(ipaddr, port);			

			// Send ACK
			evq_sender_udp->push_network(ack, dst_ipaddr,
				dst_port);

			start_timer_D();
			state = TS_COMPLETED;
		}

		// Report to TU
		evq_trans_layer->push_user(r, tuid, id);
		break;
	case TS_COMPLETED:
		// A failure has been received. So 2XX is not
		// expected anymore. Discard 2XX.
		if (r->is_success()) {
			break;
		}

		// Retransmit ACK
		evq_sender_udp->push_network(ack, dst_ipaddr, dst_port);
		break;
	default:
		break;
	}
}

void t_tc_invite::process_icmp(const t_icmp_msg &icmp) {
	t_response *r;

	switch(state) {
	case TS_CALLING:
		stop_timer_A();
		stop_timer_B();
		
		// An ICMP error indicates a kind of network problem.
		// So the server is not available. Generate an internal
		// 503 Service Unavailable repsponse to notify the TU.
		r = create_response(R_503_SERVICE_UNAVAILABLE);

		log_file->write_header("t_tc_invite::process_icmp",
			LOG_NORMAL, LOG_INFO);
		log_file->write_raw("ICMP error received.\n\n");
		log_file->write_raw("Send internal:\n");
		log_file->write_raw(r->encode());
		log_file->write_footer();

		evq_trans_layer->push_user(r, tuid, id);
		MEMMAN_DELETE(r);
		delete r;
		state = TS_TERMINATED;
		break;
	default:
		// In other states a response has been received already,
		// so this ICMP error seems to be a mismatch. Discard.
		break;
	}
}

void t_tc_invite::timeout(t_sip_timer t) {
	t_response	*r;

	assert (t == TIMER_A || t == TIMER_B || t == TIMER_D);

	switch (state) {
	case TS_CALLING:
		switch (t) {
		case TIMER_A:
			// Resend request
			evq_sender_udp->push_network(request, dst_ipaddr,
				dst_port);
			start_timer_A();
			break;
		case TIMER_B:
			stop_timer_A();
			timer_B = 0;
			// Report timer expiry to TU
			r = create_response(R_408_REQUEST_TIMEOUT);

			log_file->write_header("t_tc_invite::timeout",
				LOG_NORMAL, LOG_INFO);
			log_file->write_raw("Timer B expired.\n\n");
			log_file->write_raw("Send internal:\n");
			log_file->write_raw(r->encode());
			log_file->write_footer();

			evq_trans_layer->push_user(r, tuid, id);
			MEMMAN_DELETE(r);
			delete r;
			state = TS_TERMINATED;
			break;
		default:
			// Ignore expiry of other timers.
			// Other timers should have been stopped.
			break;
		}
		break;
	case TS_COMPLETED:
		switch (t) {
		case TIMER_D:
			timer_D = 0;
			state = TS_TERMINATED;
			break;
		default:
			// Ignore expiry of other timers.
			// Other timers should have been stopped.
			break;
		}
		break;
	default:
		// Ignore timer expiries in other states
		// Other timers should have been stopped.
		break;
	}
}

void t_tc_invite::abort(void) {
	t_response	*r;

	switch (state) {
	case TS_PROCEEDING:
		r = create_response(R_408_REQUEST_TIMEOUT, "Request Aborted");

		log_file->write_header("t_tc_invite::abort",
			LOG_NORMAL, LOG_INFO);
		log_file->write_raw("Invite transaction aborted.\n\n");
		log_file->write_raw("Send internal:\n");
		log_file->write_raw(r->encode());
		log_file->write_footer();

		evq_trans_layer->push_user(r, tuid, id);
		MEMMAN_DELETE(r);
		delete r;
		state = TS_TERMINATED;
		break;
	default:
		// Ignore abortion in other states.
		// In other states the request can be terminated in
		// a normal way.
		break;
	}
}

///////////////////////////////////////////////////////////
// RFC 3261 17.1.2
// Client non-INVITE transaction
///////////////////////////////////////////////////////////

void t_tc_non_invite::start_timer_E(void) {
	if (state == TS_PROCEEDING) duration_E = DURATION_T2;
	timer_E = transaction_mgr->start_timer(duration_E, TIMER_E, id);
	duration_E = 2 * duration_E;
	if (duration_E > DURATION_T2) duration_E = DURATION_T2;
}

void t_tc_non_invite::start_timer_F(void) {
	timer_F = transaction_mgr->start_timer(DURATION_F, TIMER_F, id);
}

void t_tc_non_invite::start_timer_K(void) {
	timer_K = transaction_mgr->start_timer(DURATION_K, TIMER_K, id);
}

void t_tc_non_invite::stop_timer_E(void) {
	if (timer_E) {
		transaction_mgr->stop_timer(timer_E);
		timer_E = 0;
	}
}

void t_tc_non_invite::stop_timer_F(void) {
	if (timer_F) {
		transaction_mgr->stop_timer(timer_F);
		timer_F = 0;
	}
}

void t_tc_non_invite::stop_timer_K(void) {
	if (timer_K) {
		transaction_mgr->stop_timer(timer_K);
		timer_K = 0;
	}
}

t_tc_non_invite::t_tc_non_invite(t_request *r, unsigned long ipaddr,
		unsigned short port, unsigned short _tuid) :
	t_trans_client(r, ipaddr, port, _tuid)
{
	assert(r->method != INVITE);

	state = TS_TRYING;
	duration_E = DURATION_E;
	start_timer_E();
	start_timer_F();
	timer_K = 0;
}

t_tc_non_invite::~t_tc_non_invite() {
	stop_timer_E();
	stop_timer_F();
	stop_timer_K();
}

void t_tc_non_invite::process_provisional(t_response *r) {
	assert(r->is_provisional());

	switch (state) {
	case TS_TRYING:
	case TS_PROCEEDING:
		t_trans_client::process_provisional(r);
		state = TS_PROCEEDING;
		// Report to TU
		evq_trans_layer->push_user(r, tuid, id);
		break;
	default:
		// Discard provisional response in other states
		break;
	}
}

void t_tc_non_invite::process_final(t_response *r) {
	assert(r->is_final());

	switch (state) {
	case TS_TRYING:
	case TS_PROCEEDING:
		t_trans_client::process_final(r);
		stop_timer_E();
		stop_timer_F();
		// Report to TU
		evq_trans_layer->push_user(r, tuid, id);
		start_timer_K();
		state = TS_COMPLETED;
		break;
	case TS_COMPLETED:
		// The received response is a retransmission.
		// AS the final response is already received this
		// retransmission can be discarded.
		// fall through
	default:
		break;
	}
}

void t_tc_non_invite::process_icmp(const t_icmp_msg &icmp) {
	t_response *r;

	switch(state) {
	case TS_TRYING:
		stop_timer_E();
		stop_timer_F();
		
		// An ICMP error indicates a kind of network problem.
		// So the server is not available. Generate an internal
		// 503 Service Unavailable repsponse to notify the TU.
		r = create_response(R_503_SERVICE_UNAVAILABLE);

		log_file->write_header("t_tc_non_invite::process_icmp",
			LOG_NORMAL, LOG_INFO);
		log_file->write_raw("ICMP error received.\n\n");
		log_file->write_raw("Send internal:\n");
		log_file->write_raw(r->encode());
		log_file->write_footer();

		evq_trans_layer->push_user(r, tuid, id);
		MEMMAN_DELETE(r);
		delete r;
		state = TS_TERMINATED;
		break;
	default:
		// In other states a response has been received already,
		// so this ICMP error seems to be a mismatch. Discard.
		break;
	}
}


void t_tc_non_invite::timeout(t_sip_timer t) {
	t_response *r;

	assert (t == TIMER_E || t == TIMER_F || t == TIMER_K);

	switch (state) {
	case TS_TRYING:
	case TS_PROCEEDING:
		switch (t) {
		case TIMER_E:
			// Resend request
			evq_sender_udp->push_network(request, dst_ipaddr,
				dst_port);
			start_timer_E();
			break;
		case TIMER_F:
			timer_F = 0;
			stop_timer_E();
			// Report timer expiry to TU
			r = create_response(R_408_REQUEST_TIMEOUT);

			log_file->write_header("t_tc_non_invite::timeout",
				LOG_NORMAL, LOG_INFO);
			log_file->write_raw("Timer F expired.\n\n");
			log_file->write_raw("Send internal:\n");
			log_file->write_raw(r->encode());
			log_file->write_footer();

			evq_trans_layer->push_user(r, tuid, id);
			MEMMAN_DELETE(r);
			delete r;
			state = TS_TERMINATED;
			break;
		default:
			// Ignore expiry of other timers.
			// Other timers should have been stopped.
			break;
		}
		break;
	case TS_COMPLETED:
		switch (t) {
		case TIMER_K:
			timer_K = 0;
			state = TS_TERMINATED;
			break;
		default:
			// Ignore expiry of other timers.
			// Other timers should have been stopped.
			break;

		}
	default:
		// Ignore timer expiries in other states
		// Other timers should have been stopped.
		break;
	}
}

void t_tc_non_invite::abort(void) {
	t_response	*r;

	switch (state) {
	case TS_TRYING:
	case TS_PROCEEDING:
		stop_timer_E();
		stop_timer_F();
		r = create_response(R_408_REQUEST_TIMEOUT, "Request Aborted");

		log_file->write_header("t_tc_non_invite::abort",
			LOG_NORMAL, LOG_INFO);
		log_file->write_raw("Non-invite transaction aborted.\n\n");
		log_file->write_raw("Send internal:\n");
		log_file->write_raw(r->encode());
		log_file->write_footer();

		evq_trans_layer->push_user(r, tuid, id);
		MEMMAN_DELETE(r);
		delete r;
		state = TS_TERMINATED;
		break;
	default:
		// Ignore abortion in other states.
		// In other states the request can be terminated in
		// a normal way.
		break;
	}
}

///////////////////////////////////////////////////////////
// RFC 3261 17.2
// Server transaction
///////////////////////////////////////////////////////////

t_trans_server::t_trans_server(t_request *r, unsigned short _tuid) :
	t_transaction(r, _tuid), resp_100_trying_sent(false)
{
	t_trans_server	*t;
	t_tid		tid_cancel = 0;

	// Report to TU
	if (request->method == CANCEL) {
		t = transaction_mgr->find_cancel_target(r);
		if (t) tid_cancel = t->get_id();
		evq_trans_layer->push_user_cancel(r, tuid, id, tid_cancel);
	} else {
		evq_trans_layer->push_user(r, tuid, id);
	}
}

void t_trans_server::process_provisional(t_response *r) {
	unsigned long	ipaddr;
	unsigned short	port;
	
	if (r->code == R_100_TRYING && resp_100_trying_sent) {
		// Send 100 Trying only once
		return;
	}

	t_transaction::process_provisional(r);
	r->hdr_via.get_response_dst(ipaddr, port);
	if (ipaddr == 0) {
		// The response cannot be sent.
		state = TS_TERMINATED;
		// Report failure to TU
		evq_trans_layer->push_failure(FAIL_TRANSPORT, id);
	} else {
		// Send response
		evq_sender_udp->push_network(r, ipaddr, port);
		
		if (r->code == R_100_TRYING) {
			resp_100_trying_sent = true;
		}
	}
}

void t_trans_server::process_final(t_response *r) {
	unsigned long	ipaddr;
	unsigned short	port;

	t_transaction::process_final(r);
	r->hdr_via.get_response_dst(ipaddr, port);

	if (ipaddr == 0) {
		// The response cannot be sent.
		state = TS_TERMINATED;
		// Report failure to TU
		evq_trans_layer->push_failure(FAIL_TRANSPORT, id);
	} else {
		// Send response
		evq_sender_udp->push_network(r, ipaddr, port);
	}
}

void t_trans_server::process_retransmission(void) {
	// nothing to do
}

// RFC 3261 17.2.3
// NOTE: retransmission of an incoming INVITE for which a 2XX response
//       has been sent already is checked by the TU.
//       see dialog::is_invite_retrans
bool t_trans_server::match(t_request *r, bool cancel) const {
	t_via &orig_top_via = request->hdr_via.via_list.front();
	t_via &recv_top_via = r->hdr_via.via_list.front();

	if (recv_top_via.rfc3261_compliant()) {
		if (orig_top_via.branch != recv_top_via.branch) return false;
		if (orig_top_via.host != recv_top_via.host) return false;
		if (orig_top_via.port != recv_top_via.port) return false;

		switch(r->method) {
		case ACK:
			// return (request->hdr_cseq.method == INVITE);
			return (request->method == INVITE);
			break;
		case CANCEL:
			if (!cancel) {
				// return (request->hdr_cseq.method ==
				//	 		r->hdr_cseq.method);
				return (request->method == r->method);
			}

			// The target of CANCEL cannot be a CANCEL request
			// return (request->hdr_cseq.method != CANCEL);
			return (request->method != CANCEL);
			break;
		default:
			// return (request->hdr_cseq.method ==
			//	 		r->hdr_cseq.method);
			return (request->method == r->method);
			break;
		}
	}

	// Matching rules for backward compatibiliy with RFC 2543
	// TODO: verify rules for matching via headers
	switch (r->method) {
	case INVITE:
		return (request->method == INVITE &&
			request->uri.sip_match(r->uri) &&
			request->hdr_to.tag == r->hdr_to.tag &&
			request->hdr_from.tag == r->hdr_from.tag &&
			request->hdr_call_id.call_id ==
					 r->hdr_call_id.call_id &&
			request->hdr_cseq.seqnr == r->hdr_cseq.seqnr &&
			orig_top_via.host == recv_top_via.host &&
			orig_top_via.port == recv_top_via.port);
		break;
	case ACK:
		return (request->method == INVITE &&
			request->uri.sip_match(r->uri) &&
			request->hdr_from.tag == r->hdr_from.tag &&
			request->hdr_call_id.call_id ==
					 r->hdr_call_id.call_id &&
			request->hdr_cseq.seqnr == r->hdr_cseq.seqnr &&
			orig_top_via.host == recv_top_via.host &&
			orig_top_via.port == recv_top_via.port &&
			final != NULL &&
			final->hdr_to.tag == r->hdr_to.tag);
		break;
	case CANCEL:
		if (cancel) {
			return (request->uri.sip_match(r->uri) &&
				request->hdr_from.tag == r->hdr_from.tag &&
				request->hdr_call_id.call_id ==
					 r->hdr_call_id.call_id &&
				request->hdr_cseq.seqnr ==
					r->hdr_cseq.seqnr &&
				request->hdr_cseq.method != CANCEL &&
				orig_top_via.host == recv_top_via.host &&
				orig_top_via.port == recv_top_via.port);
		}
		// fall through
	default:
		return (request->uri.sip_match(r->uri) &&
			request->hdr_from.tag == r->hdr_from.tag &&
			request->hdr_call_id.call_id ==
					 r->hdr_call_id.call_id &&
			request->hdr_cseq == r->hdr_cseq &&
			orig_top_via.host == recv_top_via.host &&
			orig_top_via.port == recv_top_via.port);
		break;
	}

	// Should not get here
	return false;
}

bool t_trans_server::match(t_request *r) const {
	return match(r, false);
}

bool t_trans_server::match_cancel(t_request *r) const {
	assert(r->method == CANCEL);
	return match(r, true);
}

///////////////////////////////////////////////////////////
// RFC 3261 17.2.1
// Server INVITE transaction
///////////////////////////////////////////////////////////

void t_ts_invite::start_timer_G(void) {
	timer_G = transaction_mgr->start_timer(duration_G, TIMER_G, id);
	duration_G = 2 * duration_G;
	if (duration_G > DURATION_T2) duration_G = DURATION_T2;
}

void t_ts_invite::start_timer_H(void) {
	timer_H = transaction_mgr->start_timer(DURATION_H, TIMER_H, id);
}

void t_ts_invite::start_timer_I(void) {
	timer_I = transaction_mgr->start_timer(DURATION_I, TIMER_I, id);
}

void t_ts_invite::stop_timer_G(void) {
	if (timer_G) {
		transaction_mgr->stop_timer(timer_G);
		timer_G = 0;
	}
}

void t_ts_invite::stop_timer_H(void) {
	if (timer_H) {
		transaction_mgr->stop_timer(timer_H);
		timer_H = 0;
	}
}

void t_ts_invite::stop_timer_I(void) {
	if (timer_I) {
		transaction_mgr->stop_timer(timer_I);
		timer_I = 0;
	}
}

t_ts_invite::t_ts_invite(t_request *r, unsigned short _tuid) :
	t_trans_server(r, _tuid)
{
	assert(r->method == INVITE);

	state = TS_PROCEEDING;
	ack = NULL;
	timer_G = 0;
	timer_H = 0;
	timer_I = 0;
	duration_G = DURATION_G;
}

t_ts_invite::~t_ts_invite() {
	if (ack != NULL) {
		MEMMAN_DELETE(ack);
		delete ack;
	}
	stop_timer_G();
	stop_timer_H();
	stop_timer_I();
}

void t_ts_invite::process_provisional(t_response *r) {
	assert(r->is_provisional());

	switch (state) {
	case TS_PROCEEDING:
		t_trans_server::process_provisional(r);
		break;
	default:
		// TU should not send a provisional response
		// in other states.
		assert(false);
		break;
	}
}

void t_ts_invite::process_final(t_response *r) {
	assert(r->is_final());

	switch (state) {
	case TS_PROCEEDING:
		t_trans_server::process_final(r);
		if (r->is_success()) {
			state = TS_TERMINATED;
		} else {
			start_timer_G();
			start_timer_H();
			state = TS_COMPLETED;
		}
		break;
	default:
		// No final responses are expected anymore. Discard.
		break;
	}
}

void t_ts_invite::process_retransmission(void) {
	unsigned long	ipaddr;
	unsigned short	port;

	switch (state) {
	case TS_PROCEEDING:
		// Retransmit the latest provisional response (if present)
		t_trans_server::process_retransmission();
		if (provisional.size() > 0) {
			t_response *r = provisional.back();
			r->hdr_via.get_response_dst(ipaddr, port);
			if (ipaddr == 0) {
				// The response cannot be sent.
				state = TS_TERMINATED;
				// Report failure to TU
				evq_trans_layer->push_failure(
						FAIL_TRANSPORT, id);
			} else {
				// Send response
				evq_sender_udp->push_network(r, ipaddr, port);
			}
		}
		break;
	case TS_COMPLETED:
		// Retransmit the final response
		t_trans_server::process_retransmission();
		final->hdr_via.get_response_dst(ipaddr, port);
		if (ipaddr == 0) {
			// The response cannot be sent.
			state = TS_TERMINATED;
			// Report failure to TU
			evq_trans_layer->push_failure(FAIL_TRANSPORT, id);
		} else {
			// Send response
			evq_sender_udp->push_network(final, ipaddr, port);
		}
		break;
	default:
		// Retransmissions should not happen in other states.
		// Discard.
		break;
	}
}

void t_ts_invite::timeout(t_sip_timer t) {
	unsigned long	ipaddr;
	unsigned short	port;

	assert(t == TIMER_G || t == TIMER_I || t == TIMER_H);

	switch (state) {
	case TS_COMPLETED:
		switch (t) {
		case TIMER_G:
			timer_G = 0;

			// Retransmit the final response
			final->hdr_via.get_response_dst(ipaddr, port);
			if (ipaddr == 0) {
				// The response cannot be sent.
				stop_timer_H();
				state = TS_TERMINATED;
				// Report failure to TU
				evq_trans_layer->push_failure(
						FAIL_TRANSPORT, id);
			} else {
				// Send response
				evq_sender_udp->push_network(final,
						ipaddr, port);
				start_timer_G();
			}
			break;
		case TIMER_H:
			timer_H = 0;
			stop_timer_G();
			state = TS_TERMINATED;
			// Report timer expiry to TU
			evq_trans_layer->push_failure(FAIL_TIMEOUT, id);
			break;
		default:
			// No other timers should be running. Discard.
			break;
		}
		break;
	case TS_CONFIRMED:
		switch (t) {
		case TIMER_I:
			timer_I = 0;
			state = TS_TERMINATED;
			break;
		default:
			// No other timers should be running. Discard.
			break;
		}
	default:
		// In other states no timers should be running.
		break;
	}
}

void t_ts_invite::acknowledge(t_request *ack_request) {
	assert(ack_request->method == ACK);

	switch (state) {
	case TS_COMPLETED:
		ack = (t_request *)ack_request->copy();
		stop_timer_G();
		stop_timer_H();
		start_timer_I();
		state = TS_CONFIRMED;
		// Report TU
		// ACK should not be reported to TU for non-2xx
		// evq_trans_layer->push_user(ack_request, tuid, id);
		break;
	default:
		// ACK is not expected in other states. Discard;
		break;
	}
}

///////////////////////////////////////////////////////////
// RFC 3261 17.2.2
// Server non-INVITE transaction
///////////////////////////////////////////////////////////

void t_ts_non_invite::start_timer_J(void) {
	timer_J = transaction_mgr->start_timer(DURATION_J, TIMER_J, id);
}

void t_ts_non_invite::stop_timer_J(void) {
	if (timer_J) {
		transaction_mgr->stop_timer(timer_J);
		timer_J = 0;
	}
}

t_ts_non_invite::t_ts_non_invite(t_request *r, unsigned short _tuid) :
	t_trans_server(r, _tuid)
{
	assert(r->method != INVITE);
	timer_J = 0;
	state = TS_TRYING;
}

t_ts_non_invite::~t_ts_non_invite() {
	stop_timer_J();
}

void t_ts_non_invite::process_provisional(t_response *r) {
	assert(r->is_provisional());

	switch (state) {
	case TS_TRYING:
	case TS_PROCEEDING:
		t_trans_server::process_provisional(r);
		state = TS_PROCEEDING;
		break;
	default:
		// TU should not send a provisional response
		// in other states.
		assert(false);
		break;
	}
}

void t_ts_non_invite::process_final(t_response *r) {
	assert(r->is_final());

	switch (state) {
	case TS_TRYING:
	case TS_PROCEEDING:
		t_trans_server::process_final(r);
		start_timer_J();
		state = TS_COMPLETED;
		break;
	default:
		// No final responses are expected anymore. Discard.
		break;
	}
}

void t_ts_non_invite::process_retransmission(void) {
	unsigned long	ipaddr;
	unsigned short	port;
	t_response 	*r;

	switch (state) {
	case TS_PROCEEDING:
		// Retransmit the latest provisional response
		t_trans_server::process_retransmission();
		r = provisional.back();
		r->hdr_via.get_response_dst(ipaddr, port);
		if (ipaddr == 0) {
			// The response cannot be sent.
			state = TS_TERMINATED;
			// Report failure to TU
			evq_trans_layer->push_failure(FAIL_TRANSPORT, id);
		} else {
			// Send response
			evq_sender_udp->push_network(r, ipaddr, port);
		}
		break;
	case TS_COMPLETED:
		// Retransmit the final response
		t_trans_server::process_retransmission();
		final->hdr_via.get_response_dst(ipaddr, port);
		if (ipaddr == 0) {
			// The response cannot be sent.
			stop_timer_J();
			state = TS_TERMINATED;
			// Report failure to TU
			evq_trans_layer->push_failure(FAIL_TRANSPORT, id);
		} else {
			// Send response
			evq_sender_udp->push_network(final, ipaddr, port);
		}
		break;
	default:
		// Retransmissions should not happen in other states.
		// Discard.
		break;
	}
}

void t_ts_non_invite::timeout(t_sip_timer t) {
	assert (t == TIMER_J);

	switch (state) {
	case TS_COMPLETED:
		switch (t) {
		case TIMER_J:
			timer_J = 0;
			state = TS_TERMINATED;
			break;
		default:
			break;
		}
	default:
		break;
	}
}


syntax highlighted by Code2HTML, v. 0.9.1