/*
    Copyright (C) 2005-2009  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, see <https://www.gnu.org/licenses/>.
*/

#include <cstdlib>
#include <assert.h>
#include <iostream>
#include "call_history.h"
#include "call_script.h"
#include "dialog.h"
#include "exceptions.h"
#include "line.h"
#include "log.h"
#include "phone_user.h"
#include "sub_refer.h"
#include "util.h"
#include "userintf.h"
#include "audio/rtp_telephone_event.h"
#include "audits/memman.h"
#include "im/im_iscomposing_body.h"
#include "sdp/sdp.h"
#include "sockets/socket.h"
#include "stun/stun_transaction.h"

extern t_event_queue	*evq_sender;
extern t_event_queue	*evq_trans_mgr;
extern string		user_host;
extern string		local_hostname;
extern t_phone		*phone;

// Protected

// Create a request within a dialog
// RFC 3261 12.2.1.1
t_request *t_dialog::create_request(t_method m) {
	assert(state != DS_NULL);
	t_user *user_config = phone_user->get_user_profile();

	// RFC 3261 9.1
	if (m == CANCEL) {
		t_request *r = new t_request(m);
		MEMMAN_NEW(r);
		
		assert(req_out_invite);
		t_request *orig_req = req_out_invite->get_request();
		r->hdr_to = orig_req->hdr_to;
		r->hdr_from = orig_req->hdr_from;
		r->hdr_call_id = orig_req->hdr_call_id;
		r->hdr_cseq.set_seqnr(orig_req->hdr_cseq.seqnr);
		r->hdr_cseq.set_method(CANCEL);
		r->hdr_via = orig_req->hdr_via; // RFC 3261 8.1.1.7
		r->hdr_max_forwards.set_max_forwards(MAX_FORWARDS);
		r->hdr_route = orig_req->hdr_route;
		SET_HDR_USER_AGENT(r->hdr_user_agent);
		r->uri = orig_req->uri;
		
		// RFC 3263 4
		// CANCEL for a particular SIP request MUST be sent to the same SIP
		// server that the SIP request was delivered to.
		t_ip_port ip_port;
		orig_req->get_destination(ip_port, *user_config);
		r->set_destination(ip_port);
		return r;
	}
	
	t_request *r = t_abstract_dialog::create_request(m);

	// CSeq header
	if (m == ACK) {
		assert(req_out_invite);
		
		// Local sequence number was incremented by t_abstract_dialog.
		// Decrement as it ACK does not take a new sequence number.
		local_seqnr--;

		// ACK has the same sequence number
		// as the INVITE.
		r->hdr_cseq.set_seqnr(req_out_invite->get_request()->hdr_cseq.seqnr);

		// RFC 3261 22.1
		// Authorization and Proxy-Authorization headers in INVITE
		// must be repeated in ACK
		r->hdr_authorization = req_out_invite->get_request()->
					hdr_authorization;
		r->hdr_proxy_authorization = req_out_invite->get_request()->
					hdr_proxy_authorization;
	}

	// Contact header
	t_contact_param contact;
	switch (m) {
	case REFER:
	case SUBSCRIBE:
	case NOTIFY:
		// RFC 3265 7.1, RFC 3515 2.2
		// Contact header is mandatory
		contact.uri.set_url(line->create_user_contact(h_ip2str(r->get_local_ip())));
		r->hdr_contact.add_contact(contact);
		break;
	default:
		break;
	}
	
	// Privacy header
	if (line->get_hide_user()) {
		r->hdr_privacy.add_privacy(PRIVACY_ID);
	}

	return r;
}

// NULL state. Waiting for incoming INVITE
void t_dialog::state_null(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;
	t_user *user_config = phone_user->get_user_profile();

	if (r->method != INVITE) {
		state = DS_TERMINATED;
		return;
	}
	
	// Set local tag
	if (r->hdr_to.tag.size() == 0) {
		local_tag = NEW_TAG;
	} else {
		local_tag = r->hdr_to.tag;
	}
	
	// If STUN is enabled, then first send a STUN binding request to
	// discover the IP adderss and port for media.
	if (phone->use_stun(user_config)) {
		// The STUN transaction may take a while.
		// Send 100 Trying
		resp = r->create_response(R_100_TRYING);
		resp->hdr_to.set_tag("");
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
		
		if (!stun_bind_media()) {
				// STUN request failed. Send a 500 on the INVITE.
				resp = r->create_response(R_500_INTERNAL_SERVER_ERROR);
				resp->hdr_to.set_tag(local_tag);
				line->send_response(resp, tuid, tid);
				MEMMAN_DELETE(resp);
				delete resp;
				
				state = DS_TERMINATED;
				return;
		}
	}
	
	call_id = r->hdr_call_id.call_id;

	// Initialize local seqnr
	local_seqnr = NEW_SEQNR;
	local_resp_nr = NEW_SEQNR;

	remote_tag = r->hdr_from.tag;
	local_uri = r->hdr_to.uri;
	local_display = r->hdr_to.display;
	remote_uri = r->hdr_from.uri;
	remote_display = r->hdr_from.display;

	// Set remote target URI and display name
	remote_target_uri = r->hdr_contact.contact_list.front().uri;
	remote_target_display = r->
			hdr_contact.contact_list.front().display;

	// Set route set
	if (r->hdr_record_route.is_populated()) {
		route_set = r->hdr_record_route.route_list;
	}
	
	// RFC 3261 13.2.1
	// An initial INVITE should list all supported extensions.
	// Set supported extensions
	if (r->hdr_supported.is_populated()) {
		remote_extensions.insert(r->hdr_supported.features.begin(),
				r->hdr_supported.features.end());
	}

	// Media information
	int warn_code;
	string warn_text;
	if (r->body) {
		switch(r->body->get_type()) {
		case BODY_SDP:
			if (session->process_sdp_offer((t_sdp*)r->body,
					warn_code, warn_text)) {
				session->recvd_offer = true;
				break;
			}

			// Unsupported media
			resp = r->create_response(
					R_488_NOT_ACCEPTABLE_HERE);
			resp->hdr_to.set_tag(local_tag);
			resp->hdr_warning.add_warning(t_warning(LOCAL_HOSTNAME,
					0, warn_code, warn_text));
			line->send_response(resp, tuid, tid);
			
			// Create call history record
			line->call_hist_record.start_call(r, t_call_record::DIR_IN,
				user_config->get_profile_name());
			line->call_hist_record.fail_call(resp);
			
			MEMMAN_DELETE(resp);
			delete resp;
			state = DS_TERMINATED;
			return;
		default:
			// Unsupported body type. Reject call.
			resp = r->create_response(
					R_415_UNSUPPORTED_MEDIA_TYPE);
			resp->hdr_to.set_tag(local_tag);

			// RFC 3261 21.4.13
			SET_HDR_ACCEPT(resp->hdr_accept);
			
			// Create call history record
			line->call_hist_record.start_call(r, t_call_record::DIR_IN,
				user_config->get_profile_name());
			line->call_hist_record.fail_call(resp);

			line->send_response(resp, tuid, tid);
			MEMMAN_DELETE(resp);
			delete resp;
			state = DS_TERMINATED;
			return;
		}
	}
	
	resp = r->create_response(R_180_RINGING);
	resp->hdr_to.set_tag(local_tag);

	// RFC 3261 13.3.1.1
	// A provisional response creates an early dialog, so
	// copy the Record-Route header and add a Contact
	// header.

	// Copy the Record-Route header from request to response
	if (r->hdr_record_route.is_populated()) {
		resp->hdr_record_route = r->hdr_record_route;
	}

	// Set Contact header
	t_contact_param contact;
	contact.uri.set_url(line->create_user_contact(h_ip2str(resp->get_local_ip())));
	resp->hdr_contact.add_contact(contact);

	// RFC 3262 3
	// Send 180 response reliable if needed
	if (r->hdr_require.contains(EXT_100REL) ||
	    (r->hdr_supported.contains(EXT_100REL) &&
	     (user_config->get_ext_100rel() == EXT_PREFERRED ||
	      user_config->get_ext_100rel() == EXT_REQUIRED)))
	{
		resp->hdr_require.add_feature(EXT_100REL);
		resp->hdr_rseq.set_resp_nr(++local_resp_nr);

		// RFC 3262 5
		// Create SDP offer in first reliable response if no offer
		// was received in INVITE.
		// This implentation does not create an answer in an
		// reliable 1xx response if an offer was received.
		if (!session->recvd_offer) {
			session->create_sdp_offer(resp, SDP_O_USER);
		}

		// Keep a copy of the response for retransmission
		resp_1xx_invite = (t_response *)resp->copy();

		// Start 100rel timeout and guard timers
		line->start_timer(LTMR_100REL_GUARD, get_object_id());
		line->start_timer(LTMR_100REL_TIMEOUT, get_object_id());
	}

	line->send_response(resp, req_in_invite->get_tuid(),
			req_in_invite->get_tid());
	MEMMAN_DELETE(resp);
	delete resp;
	
	ui->cb_incoming_call(user_config, line->get_line_number(), r);
	line->call_hist_record.start_call(r, t_call_record::DIR_IN,
		user_config->get_profile_name());
	
	state = DS_W4ANSWER;
}

// A provisional answer has been sent. Waiting for user to answer.
void t_dialog::state_w4answer(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;
	t_user *user_config = phone_user->get_user_profile();
	bool tear_down = false;
	bool answer_call = false;
	
	t_call_script script_in_call_failed(user_config, t_call_script::TRIGGER_IN_CALL_FAILED,
			line->get_line_number() + 1);

	switch (r->method) {
	case CANCEL:
		// Cancel the request and terminate the dialog.
		// A response on the CANCEL is already given by dialog::recvd_cancel
		resp = req_in_invite->get_request()->
				create_response(R_487_REQUEST_TERMINATED);
		resp->hdr_to.set_tag(local_tag);
		line->send_response(resp, req_in_invite->get_tuid(),
				req_in_invite->get_tid());
		line->call_hist_record.fail_call(resp);
		
		// Trigger call script
		script_in_call_failed.exec_notify(resp);
		
		MEMMAN_DELETE(resp);
		delete resp;

		ui->cb_call_cancelled(line->get_line_number());
		state = DS_TERMINATED;
		break;
	case BYE:
		// Send 200 on the BYE request
		resp = r->create_response(R_200_OK);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;

		// Send a 487 response to terminate the pending request
		resp = req_in_invite->get_request()->create_response(
						R_487_REQUEST_TERMINATED);
		resp->hdr_to.set_tag(local_tag);
		line->send_response(resp, req_in_invite->get_tuid(),
						req_in_invite->get_tid());
		line->call_hist_record.fail_call(resp);
		
		// Trigger call script
		script_in_call_failed.exec_notify(resp);
		
		MEMMAN_DELETE(resp);
		delete resp;

		ui->cb_far_end_hung_up(line->get_line_number());
		state = DS_TERMINATED;
		break;
	case PRACK:
		// RFC 3262 3
		if (respond_prack(r, tuid, tid)) {
			answer_call = answer_after_prack;

			// RFC 3262 5
			// If an offer was sent in the 1xx response, then PRACK must
			// contain an answer
			if (session->sent_offer && r->body) {
				int warn_code;
				string warn_text;
				if (r->body->get_type() != BODY_SDP) {
					// Only SDP bodies are supported
					ui->cb_unsupported_content_type(
						line->get_line_number(), r);
					tear_down = true;
				} else if (session->process_sdp_answer((t_sdp *)r->body,
						warn_code, warn_text))
				{
					session->recvd_answer = true;
					session->start_rtp();
				} else {
					// SDP answer is not supported.
					// Tear down the call.
					ui->cb_sdp_answer_not_supported(
						line->get_line_number(), warn_text);
					tear_down = true;
				}
			}

			if (session->sent_offer && !r->body) {
				ui->cb_sdp_answer_missing(line->get_line_number());
				tear_down = true;
			}
		}

		if (tear_down) {
			resp = req_in_invite->get_request()->create_response(
				R_400_BAD_REQUEST,
				"SDP answer in PRACK missing or unsupported");
			resp->hdr_to.set_tag(local_tag);		
			line->send_response(resp, req_in_invite->get_tuid(),
						req_in_invite->get_tid());
			line->call_hist_record.fail_call(resp);
			
			// Trigger call script
			t_call_script script(user_config, t_call_script::TRIGGER_IN_CALL_FAILED,
					line->get_line_number() + 1);
			script.exec_notify(resp);
			
			MEMMAN_DELETE(resp);
			delete resp;
			state = DS_TERMINATED;
		} else if (answer_call) {
			answer();
		}

		break;
	default:
		// INVITE transaction has not been completed. Deny
		// other requests within the dialog.
		resp = r->create_response(R_500_INTERNAL_SERVER_ERROR,
			"Session not yet established");
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
		break;
	}
}

void t_dialog::state_w4answer(t_line_timer timer) {
	t_ip_port ip_port;
	t_response *resp;
	t_user *user_config = phone_user->get_user_profile();
	
	t_call_script script_in_call_failed(user_config, t_call_script::TRIGGER_IN_CALL_FAILED,
			line->get_line_number() + 1);

	// RFC 3262 3
	switch(timer) {
	case LTMR_100REL_TIMEOUT:
		// Retransmit 1xx response.
		// Send the response directly to the sender thread
		// bypassing the transaction layer. As this is a retransmission
		// from the TU, the transaction layer does not need to know.
		resp_1xx_invite->get_destination(ip_port);
		if (ip_port.ipaddr == 0) {
			// This should not happen. The response has been
			// sent before so it should be possible to sent
			// it again. Ignore the timeout. When the 100rel
			// guard timer expires, the dialog will be
			// cleaned up.
			break;
		}
		evq_sender->push_network(resp_1xx_invite, ip_port);
		line->start_timer(LTMR_100REL_TIMEOUT, get_object_id());
		break;
	case LTMR_100REL_GUARD:
		line->stop_timer(LTMR_100REL_TIMEOUT, get_object_id());

		// PRACK was not received in time. Tear down the call.
		resp = req_in_invite->get_request()->create_response(
			R_500_INTERNAL_SERVER_ERROR, "100rel timeout");
		resp->hdr_to.set_tag(local_tag);
		line->send_response(resp, req_in_invite->get_tuid(),
						req_in_invite->get_tid());
		line->call_hist_record.fail_call(resp);
		
		// Trigger call script
		script_in_call_failed.exec_notify(resp);
		
		MEMMAN_DELETE(resp);
		delete resp;

		remove_client_request(&req_in_invite);
		MEMMAN_DELETE(resp_1xx_invite);
		delete resp_1xx_invite;
		resp_1xx_invite = NULL;

		state = DS_TERMINATED;
		log_file->write_report("LTMR_100REL_GUARD expired.",
				"t_dialog::state_w4answer");

		ui->cb_100rel_timeout(line->get_line_number());
		break;
	default:
		// Other timeouts are not expected. Ignore.
		break;
	}
}

// 200 OK has been sent. Waiting for ACK
void t_dialog::state_w4ack(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;
	t_user *user_config = phone_user->get_user_profile();
	bool tear_down = false;
	t_client_request *cr;
	
	t_call_script script_out_call_failed(user_config, t_call_script::TRIGGER_OUT_CALL_FAILED,
			line->get_line_number() + 1);

	switch(r->method) {
	case ACK:
		// Dialog is established now.
		line->stop_timer(LTMR_ACK_TIMEOUT, get_object_id());
		line->stop_timer(LTMR_ACK_GUARD, get_object_id());
		remove_client_request(&req_in_invite);
		MEMMAN_DELETE(resp_invite);
		delete resp_invite;
		resp_invite = NULL;

		// If no offer was received in INVITE, then an offer
		// has been sent in 200 OK or reliable 1xx (RFC 3262).
		// Therefor an answer must be present in ACK
		if (!session->recvd_offer && r->body) {
			int warn_code;
			string warn_text;
			if (r->body->get_type() != BODY_SDP) {
				// Only SDP bodies are supported
				ui->cb_unsupported_content_type(
					line->get_line_number(), r);
				tear_down = true;
			} else if (session->process_sdp_answer((t_sdp *)r->body,
					warn_code, warn_text))
			{
				session->recvd_answer = true;
				session->start_rtp();
			} else {
				// SDP answer is not supported.
				// Tear down the call.
				ui->cb_sdp_answer_not_supported(
						line->get_line_number(), warn_text);
				tear_down = true;
			}
		}

		if (!session->recvd_offer && !r->body) {
			ui->cb_sdp_answer_missing(line->get_line_number());
			tear_down = true;
		}

		if (end_after_ack) {
			ui->cb_far_end_hung_up(line->get_line_number());
			state = DS_TERMINATED;
		} else {
			state = DS_CONFIRMED;
			if (tear_down) {
				send_bye();
			}
		}

		ui->cb_call_established(line->get_line_number());
		break;
	case BYE:
		// Send 200 on the BYE request
		resp = r->create_response(R_200_OK);
		line->send_response(resp, tuid, tid);
		
		// Trigger call script
		script_out_call_failed.exec_notify(resp);
		
		MEMMAN_DELETE(resp);
		delete resp;
		
		line->call_hist_record.end_call(true);

		// The session will be ended when an ACK has been
		// received.
		end_after_ack = true;
		break;
	case PRACK:
		// RFC 3262 3
		// This is a late PRACK as the call is answered already.
		// Respond in a normal way to the PRACK
		respond_prack(r, tuid, tid);
		break;
	default:
		// Queue the request as ACK needs to be received first.
		// Note that the tuid value is not stored in the queue.
		// For an incoming request tuid is always 0.
		cr = new t_client_request(user_config, r, tid);
		MEMMAN_NEW(cr);
		inc_req_queue.push_back(cr);
		log_file->write_header("t_dialog::state_w4ack", 
				LOG_NORMAL, LOG_INFO);
		log_file->write_raw("Waiting for ACK.\n");
		log_file->write_raw("Queue incoming ");
		log_file->write_raw(method2str(r->method, r->unknown_method));
		log_file->write_endl();
		log_file->write_footer();
		break;
	}
}

void t_dialog::state_w4ack_re_invite(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;
	t_user *user_config = phone_user->get_user_profile();
	bool tear_down = false;
	
	t_call_script script_out_call_failed(user_config, t_call_script::TRIGGER_OUT_CALL_FAILED,
			line->get_line_number() + 1);

	switch(r->method) {
	case ACK:
		// re_INVITE is finished now
		line->stop_timer(LTMR_ACK_TIMEOUT, get_object_id());
		line->stop_timer(LTMR_ACK_GUARD, get_object_id());
		remove_client_request(&req_in_invite);
		MEMMAN_DELETE(resp_invite);
		delete resp_invite;
		resp_invite = NULL;

		// If no offer was received in INVITE, then an offer
		// has been sent in 200 OK or reliable 1xx (RFC 3262).
		// Therefor an answer must be present in ACK
		if (!session_re_invite->recvd_offer && r->body) {
			int warn_code;
			string warn_text;

			if (r->body->get_type() != BODY_SDP) {
				// Only SDP bodies are supported
				ui->cb_unsupported_content_type(
					line->get_line_number(), r);
				tear_down = true;
			} else if (session_re_invite->process_sdp_answer(
				(t_sdp *)r->body, warn_code, warn_text))
			{
				session_re_invite->recvd_answer = true;
			} else {
				// SDP answer is not supported.
				// Tear down the call.
				ui->cb_sdp_answer_not_supported(
						line->get_line_number(), warn_text);
				tear_down = true;
			}
		}

		if (!session_re_invite->recvd_offer && !r->body) {
			ui->cb_sdp_answer_missing(line->get_line_number());
			tear_down = true;
		}

		if (end_after_ack) {
			ui->cb_far_end_hung_up(line->get_line_number());
			state = DS_TERMINATED;
		} else {
			state = DS_CONFIRMED;
			if (tear_down) {
				send_bye();
			} else {
				// Make the new session description current
				activate_new_session();
			}
		}
		break;
	case BYE:
		// Send 200 on the BYE request
		resp = r->create_response(R_200_OK);
		line->send_response(resp, tuid, tid);
		
		// Trigger call script
		script_out_call_failed.exec_notify(resp);
		
		MEMMAN_DELETE(resp);
		delete resp;
		
		line->call_hist_record.end_call(true);

		// The session will be ended when an ACK has been
		// received.
		end_after_ack = true;
		break;
	default:
		// ACK has not been received. Handle other incoming request
		// as if we are in the confirmed state. These incoming requests
		// should not change state.
		state_confirmed(r, tuid, tid);
		assert(state == DS_W4ACK_RE_INVITE);
		break;
	}
}

// RFC 3261 13.3.1.4
void t_dialog::state_w4ack(t_line_timer timer) {
	t_ip_port ip_port;

	// NOTE: this code is also executed for re-INVITE ACK time-outs
	//       timeout handling for INVITE/re-INVITE is the same

	switch(timer) {
	case LTMR_ACK_TIMEOUT:
		// Retransmit 2xx response.
		// Send the response directly to the sender thread
		// as the INVITE transaction completed already.
		// (see RFC 3261 17.2.1)
		if (!resp_invite) break; // there is no response to send
		resp_invite->get_destination(ip_port);
		if (ip_port.ipaddr == 0) {
			// This should not happen. The response has been
			// sent before so it should be possible to sent
			// it again. Ignore the timeout. When the ACK
			// guard timer expires, the dialog will be
			// cleaned up.
			break;
		}
		evq_sender->push_network(resp_invite, ip_port);
		line->start_timer(LTMR_ACK_TIMEOUT, get_object_id());
		break;
	case LTMR_ACK_GUARD:
		line->stop_timer(LTMR_ACK_TIMEOUT, get_object_id());
		// Consider dialog as established and tear down call
		remove_client_request(&req_in_invite);
		MEMMAN_DELETE(resp_invite);
		delete resp_invite;
		resp_invite = NULL;
		state = DS_CONFIRMED;
		log_file->write_report("LTMR_ACK_GUARD expired.",
				"t_dialog::state_w4ack");
		if (end_after_ack) {
			state = DS_TERMINATED;
		} else {
			send_bye();
		}
		ui->cb_ack_timeout(line->get_line_number());
		break;
	default:
		// Other timeouts are not expected. Ignore.
		break;
	}
}

void t_dialog::state_w4ack_re_invite(t_line_timer timer) {
	state_w4ack(timer);
}

void t_dialog::state_w4re_invite_resp(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;
	t_user *user_config = phone_user->get_user_profile();
	
	t_call_script script_remote_release(user_config, t_call_script::TRIGGER_REMOTE_RELEASE,
			line->get_line_number() + 1);

	switch(r->method) {
	case BYE:
		resp = r->create_response(R_200_OK);		
		line->send_response(resp, tuid, tid);
		
		// Trigger call script
		script_remote_release.exec_notify(r);
		
		MEMMAN_DELETE(resp);
		delete resp;
		ui->cb_far_end_hung_up(line->get_line_number());
		line->call_hist_record.end_call(true);

		if (!sub_refer) {
			state = DS_TERMINATED;
		} else {
			state = DS_CONFIRMED_SUB;
			if (sub_refer->get_role() == SR_SUBSCRIBER) {
				// End subscription
				sub_refer->unsubscribe();
			}
		}
		break;
	case ACK:
		// Ignore ACK
		break;
	case OPTIONS:
		resp = line->create_options_response(r, true);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
	case PRACK:
		// RFC 3262 3
		// This is a late PRACK. Respond in a normal way.
		respond_prack(r, tuid, tid);
		break;
	case SUBSCRIBE:
		process_subscribe(r, tuid, tid);
		break;
	case NOTIFY:
		process_notify(r, tuid, tid);
		break;
	default:
		resp = r->create_response(R_500_INTERNAL_SERVER_ERROR,
			"Waiting for re-INVITE response");
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
		break;
	}
}

// In the confirmed state, requests will be responded.
void t_dialog::state_confirmed(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;
	t_user *user_config = phone_user->get_user_profile();
	
	t_call_script script_remote_release(user_config, t_call_script::TRIGGER_REMOTE_RELEASE,
			line->get_line_number() + 1);

	switch(r->method) {
	case INVITE:
		// re-INVITE
		process_re_invite(r, tuid, tid);
		break;
	case BYE:
		resp = r->create_response(R_200_OK);		
		line->send_response(resp, tuid, tid);
		
		// Trigger call script
		script_remote_release.exec_notify(r);
		
		MEMMAN_DELETE(resp);
		delete resp;
		ui->cb_far_end_hung_up(line->get_line_number());
		line->call_hist_record.end_call(true);

		if (!sub_refer) {
			state = DS_TERMINATED;
		} else {
			state = DS_CONFIRMED_SUB;
			if (sub_refer->get_role() == SR_SUBSCRIBER) {
				// End subscription
				sub_refer->unsubscribe();
			}
		}
		break;
	case ACK:
		// Ignore ACK
		break;
	case OPTIONS:
		resp = line->create_options_response(r, true);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
	case PRACK:
		// RFC 3262 3
		// This is a late PRACK. Respond in a normal way.
		respond_prack(r, tuid, tid);
		break;
	case REFER:
		process_refer(r, tuid, tid);
		break;
	case SUBSCRIBE:
		process_subscribe(r, tuid, tid);
		break;
	case NOTIFY:
		process_notify(r, tuid, tid);
		break;
	case INFO:
		process_info(r, tuid, tid);
		break;
	case MESSAGE:
		process_message(r, tuid, tid);
		break;
	default:
		resp = r->create_response(R_500_INTERNAL_SERVER_ERROR);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
		break;
	}
}

void t_dialog::state_confirmed(t_line_timer timer) {
	switch(timer) {
	case LTMR_GLARE_RETRY:
		switch(reinvite_purpose) {
		case REINVITE_HOLD:
			hold();
			break;
		case REINVITE_RETRIEVE:
			retrieve();
			line->retry_retrieve_succeeded();
			// Note that the re-INVITE is not completed here yet.
			// If re-INVITE fails then line->failed_retrieve will
			// be called later.
			break;
		default:
			assert(false);
		}

		break;
	default:
		// Other timeouts are not exepcted. Ignore.
		break;
	}
}

void t_dialog::state_confirmed_sub(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;

	switch(r->method) {
	case OPTIONS:
		resp = line->create_options_response(r, true);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
	case SUBSCRIBE:
		process_subscribe(r, tuid, tid);
		break;
	case NOTIFY:
		process_notify(r, tuid, tid);
		break;
	default:
		resp = r->create_response(R_500_INTERNAL_SERVER_ERROR);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
		break;
	}

	if (!sub_refer) {
		// The subscription has been terminated already.
		state = DS_TERMINATED;
	} else if (sub_refer->get_state() == SS_TERMINATED) {
		MEMMAN_DELETE(sub_refer);
		delete sub_refer;
		sub_refer = NULL;
		state = DS_TERMINATED;
	}
}

void t_dialog::process_re_invite(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;
	t_user *user_config = phone_user->get_user_profile();

	session_re_invite = session->create_clean_copy();

	// Media information
	int warn_code;
	string warn_text;
	if (r->body) {
		switch(r->body->get_type()) {
		case BODY_SDP:
			if (session_re_invite->
					process_sdp_offer((t_sdp*)r->body,
						warn_code, warn_text))
			{
				session_re_invite->recvd_offer = true;
				break;
			}

			// Unsupported media
			resp = r->create_response(
					R_488_NOT_ACCEPTABLE_HERE);
			resp->hdr_warning.add_warning(t_warning(LOCAL_HOSTNAME,
					0, warn_code, warn_text));
			line->send_response(resp, tuid, tid);
			MEMMAN_DELETE(resp);
			delete resp;
			
			MEMMAN_DELETE(session_re_invite);
			delete session_re_invite;
			session_re_invite = NULL;

			// Stay in the confirmed state. The sender of the
			// request has to determine if the dialog needs to
			// be torn down by sending a BYE.
			return;
		default:
			// Unsupported body type. Reject call.
			resp = r->create_response(
					R_415_UNSUPPORTED_MEDIA_TYPE);

			// RFC 3261 21.4.13
			SET_HDR_ACCEPT(resp->hdr_accept);

			line->send_response(resp, tuid, tid);
			MEMMAN_DELETE(resp);
			delete resp;
			
			MEMMAN_DELETE(session_re_invite);
			delete session_re_invite;
			session_re_invite = NULL;

			// Stay in the confirmed state.
			return;
		}
	}
	
	// If STUN is enabled, then first send a STUN binding request to
	// discover the IP adderss and port for media if no RTP stream
	// is currently active.
	if (phone->use_stun(user_config) && !session->is_rtp_active()) {
		// The STUN transaction may take a while.
		// Send 100 Trying
		resp = r->create_response(R_100_TRYING);
		resp->hdr_to.set_tag("");
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
		
		if (!stun_bind_media()) {
			// STUN request failed. Send a 500 on the INVITE.
			resp = r->create_response(R_500_INTERNAL_SERVER_ERROR);
			resp->hdr_to.set_tag(local_tag);
			line->send_response(resp, tuid, tid);
			MEMMAN_DELETE(resp);
			delete resp;
				
			state = DS_TERMINATED;
			return;
		}
	}

	// Refresh target
	if (r->hdr_contact.is_populated() &&
	    r->hdr_contact.contact_list.size() > 0)
	{
		remote_target_uri = r->hdr_contact.contact_list.front().uri;
		remote_target_display = r->
			hdr_contact.contact_list.front().display;
	}

	// Send 200 OK
	resp_invite = r->create_response(R_200_OK);
	resp_invite->hdr_to.set_tag(local_tag);

	// Set Contact header
	t_contact_param contact;
	contact.uri.set_url(line->create_user_contact(h_ip2str(resp_invite->get_local_ip())));
	resp_invite->hdr_contact.add_contact(contact);

	// Set Allow and Supported headers
	SET_HDR_ALLOW(resp_invite->hdr_allow, user_config);
	SET_HDR_SUPPORTED(resp_invite->hdr_supported, user_config);

	// RFC 3261 13.3.1.4
	// Create SDP offer if no offer was received in INVITE and no offer
	// was sent in a reliable 1xx response (RFC 3262 5).
	// Otherwise create an SDP answer.
	if (!session_re_invite->recvd_offer && !session_re_invite->sent_offer) {
		session_re_invite->create_sdp_offer(resp_invite, SDP_O_USER);
	} else {
		session_re_invite->create_sdp_answer(resp_invite, SDP_O_USER);
	}

	line->send_response(resp_invite, tuid, tid);
	line->start_timer(LTMR_ACK_GUARD, get_object_id());
	line->start_timer(LTMR_ACK_TIMEOUT, get_object_id());

	state = DS_W4ACK_RE_INVITE;
}

void t_dialog::process_refer(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;
	t_user *user_config = phone_user->get_user_profile();
	t_contact_param contact;
	
	refer_accepted = true;

	// RFC 3515
	if (sub_refer || !user_config->get_allow_refer()) {
		// A reference is already in progress or REFER is not
		// allowed.
		resp = r->create_response(R_603_DECLINE);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
		refer_accepted = false;
		return;
	}

	// Check if the URI scheme is supported
	if (r->hdr_refer_to.uri.get_scheme() != "sip") {
		resp = r->create_response(R_416_UNSUPPORTED_URI_SCHEME);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
		refer_accepted = false;
		return;
	}

	resp = r->create_response(R_202_ACCEPTED);

	// RFC 3515 2.2
	// Contact header is mandatory
	contact.uri.set_url(line->create_user_contact(h_ip2str(resp->get_local_ip())));
	resp->hdr_contact.add_contact(contact);

	if (r->hdr_refer_sub.is_populated() && !r->hdr_refer_sub.create_refer_sub) {
		// RFC 4488 4
		resp->hdr_refer_sub.set_create_refer_sub(false);
	}
	line->send_response(resp, tuid, tid);
	MEMMAN_DELETE(resp);
	delete resp;

	if (r->hdr_refer_sub.is_populated() && !r->hdr_refer_sub.create_refer_sub) {
		// RFC 4488
		// The REFER-issuer requested not to create an implicit refer
		// subscription.
		log_file->write_report(
			"REFER-issuer requested not to create a refer subscription.",
			"t_dialog::process_refer");
	} else {
		// RFC 3515
		// The event header of a NOTIFY to a first REFER MAY
		// include the id paramter. NOTIFY's to subsequent
		// REFERs MUST include the id parameter (CSeq from REFER).
		sub_refer = new t_sub_refer(this, SR_NOTIFIER,
			ulong2str(r->hdr_cseq.seqnr));
		MEMMAN_NEW(sub_refer);
	
		// Send immediate NOTIFY
		resp = new t_response(R_100_TRYING);
		MEMMAN_NEW(resp);
		if (user_config->get_ask_user_to_refer()) {
			// If the user has to grant permission, then the
			// subscription is pending.
			sub_refer->send_notify(resp, SUBSTATE_PENDING);
		} else {
			sub_refer->send_notify(resp, SUBSTATE_ACTIVE);
		}
		MEMMAN_DELETE(resp);
		delete resp;
	}

	// Ask permission to refer
	if (user_config->get_ask_user_to_refer()) {
		if (r->hdr_referred_by.is_populated()) {
			ui->cb_ask_user_to_refer(user_config,
				r->hdr_refer_to.uri,
				r->hdr_refer_to.display,
				r->hdr_referred_by.uri,
				r->hdr_referred_by.display);
		} else {
			ui->cb_ask_user_to_refer(user_config,
				r->hdr_refer_to.uri,
				r->hdr_refer_to.display,
				t_url(), "");
		}
	} else {
		ui->send_refer_permission(true);
	}
	
	// NOTE: refer_accepted = true, though the answer to permission
	//       is not given yet. So this means, that the refer is not
	//       rejected at this moment. It may be rejected by the user.
}

void t_dialog::recvd_refer_permission(bool permission, t_request *r) {
	t_response *resp;
	
	// NOTE: if the REFER-issuer requested not to create a refer
	// subscription (RFC 4488), then no NOTIFY can be sent to signal
	// the rejection.
	if (!permission && sub_refer) {
		// User denied REFER
		// RFC 3515 2.4.5
		resp = new t_response(R_603_DECLINE);
		MEMMAN_NEW(resp);
		sub_refer->send_notify(resp, SUBSTATE_TERMINATED,
				EV_REASON_REJECTED);
		MEMMAN_DELETE(resp);
		delete resp;
	}
	
	refer_accepted = permission;
}

void t_dialog::process_subscribe(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;

	if (sub_refer && sub_refer->match(r)) {
		sub_refer->recv_subscribe(r, tuid, tid);
		if (sub_refer->get_state() == SS_TERMINATED) {
			MEMMAN_DELETE(sub_refer);
			delete sub_refer;
			sub_refer = NULL;
		}

		return;
	}

	resp = r->create_response(R_481_TRANSACTION_NOT_EXIST,
			REASON_481_SUBSCRIPTION_NOT_EXIST);
	line->send_response(resp, tuid, tid);
	MEMMAN_DELETE(resp);
	delete resp;
}

void t_dialog::process_notify(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;

	if (!sub_refer &&
	    (refer_state == REFST_W4RESP || refer_state == REFST_W4NOTIFY))
	{
		// First NOTIFY after sending a REFER
		sub_refer = new t_sub_refer(this, SR_SUBSCRIBER, r->hdr_event.id);
		MEMMAN_NEW(sub_refer);
		refer_state = REFST_PENDING;
	}

	if (sub_refer && sub_refer->match(r)) {
		sub_refer->recv_notify(r, tuid, tid);
		if (sub_refer->get_state() == SS_TERMINATED) {
			// Set the refer state to NULL before calling the UI
			// call back functions as the user interface might use
			// the refer state to render the correct status to the
			// user.
			refer_state = REFST_NULL;
		
			// Determine outcome of the reference
			switch(sub_refer->get_sr_result()) {
			case SRR_INPROG:
				// The outcome of the reference is unknown.
				// Treat it as a success as no new info will
				// come to the referrer.
				refer_succeeded = true;
				ui->cb_refer_result_inprog(line->get_line_number());
				break;
			case SRR_FAILED:
				refer_succeeded = false;
				ui->cb_refer_result_failed(line->get_line_number());
				break;
			case SRR_SUCCEEDED:
				refer_succeeded = true;
				ui->cb_refer_result_success(line->get_line_number());
				break;
			default:
				assert(false);
			}

			MEMMAN_DELETE(sub_refer);
			delete sub_refer;
			sub_refer = NULL;
		} else if (!sub_refer->is_pending()) {
			refer_state = REFST_ACTIVE;
		}

		return;
	}

	// RFC 3265 3.2.4
	resp = r->create_response(R_481_TRANSACTION_NOT_EXIST,
			REASON_481_SUBSCRIPTION_NOT_EXIST);
	line->send_response(resp, tuid, tid);
	MEMMAN_DELETE(resp);
	delete resp;
}

void t_dialog::process_info(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;
	
	// RFC 2976 2.2
	// A 200 OK response MUST be sent by a UAS for an INFO request with
        // no message body if the INFO request was successfully received for
        // an existing call.
	if (!r->body) {
		resp = r->create_response(R_200_OK);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
		
		return;
	}
	
	if (r->body->get_type() != BODY_DTMF_RELAY) {
		resp = r->create_response(R_415_UNSUPPORTED_MEDIA_TYPE);
		resp->hdr_accept.add_media(t_media("application", "dtmf-relay"));
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
		
		return;
	}
	
	char dtmf_signal = ((t_sip_body_dtmf_relay *)r->body)->signal;
	if (!is_valid_dtmf_sym(dtmf_signal)) {
		resp = r->create_response(R_400_BAD_REQUEST, "Invalid DTMF signal");
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
		
		return;
	}
	
	resp = r->create_response(R_200_OK);
	line->send_response(resp, tuid, tid);
	MEMMAN_DELETE(resp);
	delete resp;
	
	ui->cb_dtmf_detected(line->get_line_number(), char2dtmf_ev(dtmf_signal));
}

void t_dialog::process_message(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;
	t_user *user_config = phone_user->get_user_profile();
	
	log_file->write_report("Received in-dialog MESSAGE.",
		"t_dialog::process_message", LOG_NORMAL, LOG_DEBUG);
		
	if (!r->body || !MESSAGE_CONTENT_TYPE_SUPPORTED(*r)) {
		resp = r->create_response(R_415_UNSUPPORTED_MEDIA_TYPE);
		// RFC 3261 21.4.13
		SET_MESSAGE_HDR_ACCEPT(resp->hdr_accept);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
		
		return;
	}
	
	if (r->body && r->body->get_type() == BODY_IM_ISCOMPOSING_XML) {
		// Message composing indication
		t_im_iscomposing_xml_body *sb = dynamic_cast<t_im_iscomposing_xml_body *>(r->body);
		im::t_composing_state state = im::string2composing_state(sb->get_state());
		time_t refresh = sb->get_refresh();
		
		ui->cb_im_iscomposing_request(line->get_user(), r, state, refresh);
		resp = r->create_response(R_200_OK);
	} else {
		// Instant message
		bool accepted = ui->cb_message_request(line->get_user(), r);
		if (accepted) {
			resp = r->create_response(R_200_OK);
		} else {
			if (user_config->get_im_max_sessions() == 0) {
				resp = r->create_response(R_603_DECLINE);
			} else {
				resp = r->create_response(R_486_BUSY_HERE);
			}
		}
	}
	
	line->send_response(resp, tuid, tid);
	MEMMAN_DELETE(resp);
	delete resp;
}

// INVITE sent. Waiting for a first non-100 response.
void t_dialog::state_w4invite_resp(t_response *r, t_tuid tuid, t_tid tid) {
	if (r->hdr_cseq.method != INVITE) return;
	t_user *user_config = phone_user->get_user_profile();

	// 1XX (except 100) and 2XX establish the dialog.
	// Update the state for dialog establishment.
	// RFC 3261 12.1.2
	switch (r->get_class()) {
	case R_1XX:
		if (r->code == R_100_TRYING) break;
		
		// RFC 3262 4
		// Discard retransmissions and out-of-sequence reliable
		// provisional responses.
		if (must_discard_100rel(r)) return;

		// fall thru
	case R_2XX:
		// Set remote tag
		remote_tag = r->hdr_to.tag;

		create_route_set(r);
		create_remote_target(r);

		// Set remote URI and display name
		remote_uri = r->hdr_to.uri;
		remote_display = r->hdr_to.display;

		process_1xx_2xx_invite_resp(r);
		break;
	default:
		break;
	}

	// RFC 3262
	// Send PRACK if required
	send_prack_if_required(r);
	
	t_call_script script_out_call_answered(user_config,
			t_call_script::TRIGGER_OUT_CALL_ANSWERED,
			line->get_line_number() + 1);
	t_call_script script_out_call_failed(user_config, 
			t_call_script::TRIGGER_OUT_CALL_FAILED,
			line->get_line_number() + 1);

	switch (r->get_class()) {
	case R_1XX:
		// Provisional response received.
		line->ci_set_last_provisional_reason(r->reason);
		ui->cb_provisional_resp_invite(line->get_line_number(), r);
		if (r->code > R_100_TRYING && r->hdr_to.tag.size() > 0) {
			state = DS_EARLY;
		} else {
			state = DS_W4INVITE_RESP2;
		}

		// User indicated that the request should be cancelled.
		// Now that the first provisional response has been received,
		// a CANCEL can be sent.
		if (request_cancelled) {
			send_cancel(true);
		}

		break;
	case R_2XX:
		// Stop cancel guard timer if it was running
		line->stop_timer(LTMR_CANCEL_GUARD, get_object_id());
	
		// Success received.
		ack_2xx_invite(r);

		// Check for REFER support
		// If the Allow header is not present then assume REFER
		// is supported.
		if (!r->hdr_allow.is_populated() ||
		    r->hdr_allow.contains_method(REFER))
		{
			line->ci_set_refer_supported(true);
		}
		
		// Trigger call script
		script_out_call_answered.exec_notify(r);

		ui->cb_call_answered(user_config, line->get_line_number(), r);
		line->call_hist_record.answer_call(r);
		state = DS_CONFIRMED;

		if (request_cancelled) {
			// User indicated that the request should be cancelled,
			// but no response was received yet. A final response
			// has been received. Instead of CANCEL a BYE will be
			// sent now.
			send_bye();
		} else if (end_after_2xx_invite) {
			// Or user cancelled the request already, but the 2XX
			// glared with CANCEL.
			log_file->write_report("CANCEL / 2XX INVITE glare.",
				"t_dialog::state_w4invite_resp");
			send_bye();
		}

		break;
	case R_3XX:
	case R_4XX:
	case R_5XX:
	case R_6XX:
	default:
		// Stop cancel guard timer if it was running
		line->stop_timer(LTMR_CANCEL_GUARD, get_object_id());
	
		// Final response (failure) received.
		// Treat unknown response classes as failure.
		
		// Trigger call script
		script_out_call_failed.exec_notify(r);
	
		ui->cb_stop_call_notification(line->get_line_number());
		ui->cb_call_failed(user_config, line->get_line_number(), r);
		line->call_hist_record.fail_call(r);
		remove_client_request(&req_out_invite);
		state = DS_TERMINATED;
		break;
	}

	// Notify progress to the referror if this is a referred call
	if (is_referred_call) {
		get_phone()->notify_refer_progress(r, line->get_line_number());
	}
}

void t_dialog::state_w4invite_resp(t_line_timer timer) {
	switch (timer) {
	case LTMR_CANCEL_GUARD:
		log_file->write_report("Timer LTMR_CANCEL_GUARD expired.",
				"t_dialog::state_w4invite_resp", LOG_NORMAL, LOG_WARNING);
	
		// CANCEL has been responded to, but 487 on INVITE was never
		// received. Abort the INVITE transaction.
		if (req_out_invite) {
			t_tid _tid = req_out_invite->get_tid();
			if (_tid > 0) {
				evq_trans_mgr->push_abort_trans(_tid);
			}
		}
		break;
	default:
		// Ignore other timeouts
		break;
	}
}

// INVITE response sent. At least 1 provisional response (not 100 Trying)
// received.
void t_dialog::state_early(t_response *r, t_tuid tuid, t_tid tid) {
	if (r->hdr_cseq.method != INVITE) return;
	t_user *user_config = phone_user->get_user_profile();

	switch (r->get_class()) {
	case R_1XX:
		// RFC 3262 4
		// Discard retransmissiona and out-of-sequence reliable
		// provisional responses.
		if (must_discard_100rel(r)) return;

		// fall thru
	case R_2XX:
		create_route_set(r);
		create_remote_target(r);
		process_1xx_2xx_invite_resp(r);
		break;
	default:
		break;
	}

	// RFC 3262
	// Send PRACK if required
	send_prack_if_required(r);
	
	t_call_script script_out_call_answered(user_config, 
			t_call_script::TRIGGER_OUT_CALL_ANSWERED,
			line->get_line_number() + 1);
	t_call_script script_out_call_failed(user_config, 
			t_call_script::TRIGGER_OUT_CALL_FAILED,
			line->get_line_number() + 1);

	switch (r->get_class()) {
	case R_1XX:
		// Provisional response received.
		line->ci_set_last_provisional_reason(r->reason);
		ui->cb_provisional_resp_invite(line->get_line_number(), r);

		if (request_cancelled) {
			send_cancel(true);
		}

		break;
	case R_2XX:
		// Stop cancel guard timer if it was running
		line->stop_timer(LTMR_CANCEL_GUARD, get_object_id());
		
		// Success received.
		ack_2xx_invite(r);

		// Check for REFER support
		// If the Allow header is not present then assume REFER
		// is supported.
		if (!r->hdr_allow.is_populated() ||
		    r->hdr_allow.contains_method(REFER))
		{
			line->ci_set_refer_supported(true);
		}
		
		// Trigger call script
		script_out_call_answered.exec_notify(r);

		ui->cb_call_answered(user_config, line->get_line_number(), r);
		line->call_hist_record.answer_call(r);
		state = DS_CONFIRMED;

		if (request_cancelled) {
			// User indicated that the request should be cancelled,
			// but no response was received yet. A final response
			// has been received. Instead of CANCEL a BYE will be
			// sent now.
			send_bye();
		} else if (end_after_2xx_invite) {
			// Or user cancelled the request already, but the 2XX
			// glared with CANCEL.
			log_file->write_report("CANCEL / 2XX INVITE glare.",
				"t_dialog::state_w4invite_resp");
			send_bye();
		}

		break;
	case R_3XX:
	case R_4XX:
	case R_5XX:
	case R_6XX:
	default:
		// Stop cancel guard timer if it was running
		line->stop_timer(LTMR_CANCEL_GUARD, get_object_id());
		
		// Final response (failure) received.
		// Treat unknown response classes as failure.

		// Trigger call script
		script_out_call_failed.exec_notify(r);
		
		ui->cb_stop_call_notification(line->get_line_number());
		ui->cb_call_failed(user_config, line->get_line_number(), r);
		line->call_hist_record.fail_call(r);
		remove_client_request(&req_out_invite);
		state = DS_TERMINATED;
		break;
	}

	// Notify progress to the referror if this is a referred call
	if (is_referred_call) {
		get_phone()->notify_refer_progress(r, line->get_line_number());
	}
}

void t_dialog::state_early(t_line_timer timer) {
	switch (timer) {
	case LTMR_CANCEL_GUARD:
		log_file->write_report("Timer LTMR_CANCEL_GUARD expired.",
				"t_dialog::state_early", LOG_NORMAL, LOG_WARNING);
	
		// CANCEL has been responded to, but 487 on INVITE was never
		// received. Abort the INVITE transaction.
		if (req_out_invite) {
			t_tid _tid = req_out_invite->get_tid();
			if (_tid > 0) {
				evq_trans_mgr->push_abort_trans(_tid);
			}
		}
		break;
	default:
		// Ignore other timeouts
		break;
	}
}

// BYE sent. Waiting for response.
void t_dialog::state_w4bye_resp(t_response *r, t_tuid tuid, t_tid tid) {
	if (r->hdr_cseq.method != BYE) return;

	switch (r->get_class()) {
	case R_1XX:
		// Provisional response received. Wait for final response.
		break;
	default:
		// All final responses terminate the dialog.
		remove_client_request(&req_out);
		if (!sub_refer) {
			state = DS_TERMINATED;
		} else {
			state = DS_CONFIRMED_SUB;
			if (sub_refer->get_role() == SR_SUBSCRIBER) {
				// End subscription
				sub_refer->unsubscribe();
			}
		}
		break;
	}
}

void t_dialog::state_w4bye_resp(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;

	switch(r->method) {
	case BYE:
		resp = r->create_response(R_200_OK);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;

		// A BYE glare situation. Keep waiting for the BYE
		// response.
		break;
	default:
		resp = r->create_response(R_500_INTERNAL_SERVER_ERROR);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
		break;
	}
}

// Confirmed dialog. Responses are for mid-dialog requests.
void t_dialog::state_confirmed_resp(t_response *r, t_tuid tuid, t_tid tid) {
	// 1XX responses are not expected. If they are received
	// then simply ignore them.
	if (r->is_provisional()) return;

	switch (r->hdr_cseq.method) {
	case OPTIONS:
		ui->cb_options_response(r);
		remove_client_request(&req_out);
		break;
	case REFER:
		remove_client_request(&req_refer);

		if (refer_state != REFST_W4RESP) {
			// NOTIFY has already been received. No need to
			// process the REFER response anymore. Interesting
			// issue might be: what if NOTIFY has been received and
			// now a failure response comes in?
			break;
		}

		if (!r->is_success()) {
			// REFER failed
			refer_state = REFST_NULL;
			refer_succeeded = false;
			
			// KLUDGE: only signal REFER failure in case of
			//         non-408/481 responses. These responses
			//         clear the line, so the upper layers should not
			//         take action on the failed refer.
			if (r->code != R_408_REQUEST_TIMEOUT ||
	    		    r->code == R_481_TRANSACTION_NOT_EXIST)
	    		{
				out_refer_req_failed = true;
			}
			
			ui->cb_refer_failed(line->get_line_number(), r);
			break;
		}

		refer_state = REFST_W4NOTIFY;
		break;
	case INFO:
		remove_client_request(&req_info);
		
		if (!dtmf_queue.empty()) {
			char digit = dtmf_queue.front();
			dtmf_queue.pop();
			send_dtmf(digit, false, true);
		}
		
		break;
	default:
		// The received response should match the pending request.
		// So this point should never be reached.
		assert(false);
		break;
	}

	// RFC 3261 12.2.1.2
	// If a mid-dialog request is timed out, or the call/transaction
	// does not exist anymore at the server, then terminate the
	// dialog.
	if (r->code == R_408_REQUEST_TIMEOUT ||
	    r->code == R_481_TRANSACTION_NOT_EXIST)
	{
		send_bye();
	}
}

void t_dialog::state_w4re_invite_resp(t_response *r, t_tuid tuid, t_tid tid) {
	if (r->hdr_cseq.method != INVITE) return;

	switch (r->get_class()) {
	case R_1XX:
		if (r->code == R_100_TRYING) break;

		// RFC 3262 4
		// Discard retransmissiona and out-of-sequence reliable
		// provisional responses.
		if (must_discard_100rel(r)) return;

		if (state == DS_W4RE_INVITE_RESP2) {
			// RFC 3262
			// Discard retransmissions and out-of-order
			// reliable provisional responses.
			if (must_discard_100rel(r)) return;
		}

		// RFC 3262
		// Send PRACK if required
		send_prack_if_required(r);

		// fall thru
	case R_2XX:
		// Process SDP answer if answer is present and no
		// answer has been received yet.
		if (!session_re_invite->recvd_answer && r->body) {
			int warn_code;
			string warn_text;

			if (r->body->get_type() != BODY_SDP) {
				// Only SDP bodies are supported
				ui->cb_unsupported_content_type(
					line->get_line_number(), r);
				request_cancelled = true;
			} else if (session_re_invite->
				   process_sdp_answer((t_sdp *)r->body,
				   	warn_code, warn_text))
			{
				session_re_invite->recvd_answer = true;
			} else {
				// SDP answer is not supported. Cancel
				// the INVITE.
				request_cancelled = true;
				ui->cb_sdp_answer_not_supported(
						line->get_line_number(), warn_text);
				break;
			}
		}

		// This implementation always sends an offer in
		// INVITE. So an answer must be in a 2XX response
		// as PRACK is not supported.
		if (r->get_class() == R_2XX && !r->body) {
			request_cancelled = true;
			ui->cb_sdp_answer_missing(line->get_line_number());
			break;
		}

		// Refresh target URI and display name
		if (r->get_class() == R_2XX &&
		    r->hdr_contact.is_populated() &&
	    	    r->hdr_contact.contact_list.size() > 0)
		{
			remote_target_uri = r->
				hdr_contact.contact_list.front().uri;
			remote_target_display = r->
				hdr_contact.contact_list.front().display;
		}

		break;
	default:
		break;
	}

	switch (r->get_class()) {
	case R_1XX:
		// Provisional response received.
		state = DS_W4RE_INVITE_RESP2;

		// Start re-INVITE guard timer (no RFC requirement)
		line->start_timer(LTMR_RE_INVITE_GUARD, get_object_id());

		// User indicated that the request should be cancelled.
		// Now that the first provional response has been received,
		// a CANCEL can be sent.
		if (request_cancelled) {
			send_cancel(true);
		}

		break;
	case R_2XX:
		// Success received.
		line->stop_timer(LTMR_RE_INVITE_GUARD, get_object_id());

		ack_2xx_invite(r);
		ui->cb_reinvite_success(line->get_line_number(), r);
		state = DS_CONFIRMED;

		if (request_cancelled) {
			// User indicated that the request should be cancelled,
			// but no response was received yet. A final response
			// has been received. Instead of CANCEL a BYE will be
			// sent now.
			send_bye();
		} else if (end_after_2xx_invite) {
			// Or user cancelled the request already, but the 2XX
			// glared with CANCEL.
			log_file->write_report("CANCEL / 2XX INVITE glare.",
				"t_dialog::state_w4invite_resp");
			send_bye();
		} else {
			// Make the re-INIVTE session info the current info
			activate_new_session();
		}

		break;
	case R_3XX:
	case R_4XX:
	case R_5XX:
	case R_6XX:
	default:
		// Final response (failure) received.
		// Treat unknown response classes as failure.
		line->stop_timer(LTMR_RE_INVITE_GUARD, get_object_id());
		ui->cb_reinvite_failed(line->get_line_number(), r);
		remove_client_request(&req_out_invite);

		// RFC 3261 14.1
		// delete re-INVITE session info. Old session info
		// stays as re-INVITE failed.
		MEMMAN_DELETE(session_re_invite);
		delete session_re_invite;
		session_re_invite = NULL;

		state = DS_CONFIRMED;

		switch(reinvite_purpose) {
		case REINVITE_HOLD:
			// A call hold may not fail for the user as
			// this cause problems with soundcard access and
			// showing line status in the GUI. Even though re-INVITE
			// failed, the RTP still stopped. So simply indicated
			// that the hold failed, such that a subsequent retrieve
			// can simply restart the RTP.
			hold_failed = true;
			break;
		case REINVITE_RETRIEVE:
			line->failed_retrieve();
			if (r->code != R_491_REQUEST_PENDING) {
				ui->cb_retrieve_failed(line->get_line_number(), r);
			}
			break;
		default:
			assert(false);
		}

		// RFC 3261 14.1
		// Start wait timer before retrying a re-INVITE after a
		// glare.
		if (r->code == R_491_REQUEST_PENDING) {
			line->start_timer(LTMR_GLARE_RETRY, get_object_id());
		}

		// RFC 3261 14.1
		if (r->code == R_408_REQUEST_TIMEOUT ||
		    r->code == R_481_TRANSACTION_NOT_EXIST)
		{
			send_bye();
		}
		break;
	}
}

void t_dialog::state_w4re_invite_resp(t_line_timer timer) {
	switch(timer) {
	case LTMR_RE_INVITE_GUARD:
		// Abort the INVITE as the user cannot terminate
		// it in a normal way.
		if (req_out_invite) {
			t_tid _tid = req_out_invite->get_tid();
			if (_tid > 0) {
				evq_trans_mgr->push_abort_trans(_tid);
			}
		} else {
			// Consider this as if a 408 Timeout response has
			// been received. Terminate the dialog.
			send_bye();
		}
		break;
	default:
		break;
	}
}

void t_dialog::activate_new_session(void) {
	if (session->equal_audio(*session_re_invite)) {
		log_file->write_report("SDP in re-INVITE is a noop.",
			"t_dialog::activate_new_session");
	
		MEMMAN_DELETE(session_re_invite);
		delete session_re_invite;
		session_re_invite = NULL;
		return;
	}
	
	log_file->write_report("Renew session as specified by SDP in re-INVITE.",
		"t_dialog::activate_new_session");

	// Stop current session
	MEMMAN_DELETE(session);
	delete session;

	// Create new session
	session = session_re_invite;
	session_re_invite = NULL;
	session->start_rtp();
}

void t_dialog::process_1xx_2xx_invite_resp(t_response *r) {
	t_user *user_config = phone_user->get_user_profile();
	
	// Process SDP answer if answer is present and no
	// answer has been received yet.
	if (r->body) {
		int warn_code;
		string warn_text;

		if (r->body->get_type() != BODY_SDP) {
			// Only SDP bodies are supported
			ui->cb_unsupported_content_type(line->get_line_number(), r);
			request_cancelled = true;
		} else if (!session->recvd_answer || 
		           (user_config->get_allow_sdp_change() && 
		            ((t_sdp *)r->body)->origin.session_version !=
		            session->dst_sdp_version))
		{
			// Only process SDP if no SDP was received yet (RFC 3261
			// 13.3.1. Or process SDP if overridden by the
			// allow_sdp_change setting in the user profile.
			// A changed SDP must have a new version number (RFC 3264)
			if (session->process_sdp_answer((t_sdp *)r->body,
					warn_code, warn_text))
			{
				// If this is a changed SDP, then stop the
				// current RTP stream based on the previous SDP.
				if (session->recvd_answer) session->stop_rtp();
				
				session->recvd_answer = true;
	
				// The following code part handles the ugly interaction
				// between forking and early media (Vonage uses this).
				// In case of forking 1xx responses with SDP may com
				// from different destinations. Only the first 1xx will
				// create a media stream. Media streams on other legs cannot
				// be created as that would give sound conflicts.
				// When a 2xx response with SDP is received, an early media
				// stream on another leg must be killed.
				// Due to forking multiple 2xx repsonses from different
				// destinations may be received. Only the first 2xx response
				// will create a media session. The other dialogs receiving
				// a 2xx will be released immediately anyway (see line.cpp).
				bool start_media = true;
				t_dialog *d = line->get_dialog_with_active_session();
				if (d != NULL) {
					if (r->get_class() == R_2XX &&
					    d->get_state() != DS_CONFIRMED)
					{
						log_file->write_header(
							"t_dialog::process_1xx_2xx_invite_resp");
						log_file->write_raw(
							"Kill early media on another dialog, id=");
						log_file->write_raw(d->get_object_id());
						log_file->write_endl();
						log_file->write_footer();
						
						d->kill_rtp();
					} else {
						log_file->write_header(
							"t_dialog::process_1xx_2xx_invite_resp");
						log_file->write_raw(
							"Cannot start media as another dialog (id=");
						log_file->write_raw(d->get_object_id());
						log_file->write_raw(") already has media.\n");
						log_file->write_footer();
					
						start_media = false;
					}
				}
				
				if (start_media) {
					if (r->is_provisional()) {
						log_file->write_report("Starting early media.",
							"t_dialog::process_1xx_2xx_invite_resp");
					}
		
					// Stop locally played tones to free the soundcard
					// for the voice stream
					ui->cb_stop_call_notification(line->get_line_number());
		
					session->start_rtp();
				}
			} else {
				// SDP answer is not supported. Cancel
				// the INVITE.
				request_cancelled = true;
				ui->cb_sdp_answer_not_supported(
						line->get_line_number(), warn_text);
			}
		}
	} else if (r->code == R_180_RINGING &&
	           !ringing_received && !session->recvd_answer)
	{
		// There is no SDP and far-end indicated that it is ringing
		// so generate ring back tone locally.
		ui->cb_play_ringback(user_config);
		ringing_received = true;
	}

	// This implementation always sends an offer in
	// INVITE. So an answer must be in a 2XX response if
	// no answer has been received in a provisional response.
	if (!session->recvd_answer && r->get_class() == R_2XX && !r->body) {
		request_cancelled = true;
		ui->cb_sdp_answer_missing(line->get_line_number());
	}
	
	// RFC 3261 13.3.1.4
	// A 2XX response to an INVITE should contain a Supported header
	// listing all supported extensions.
	// Set extensions supported by remote party
	if (r->get_class() == R_2XX && r->hdr_supported.is_populated()) {
		remote_extensions.insert(r->hdr_supported.features.begin(),
				r->hdr_supported.features.end());
	}
}

void t_dialog::ack_2xx_invite(t_response *r) {
	t_ip_port ip_port;
	t_user *user_config = phone_user->get_user_profile();

	if (ack) {
		// delete previous cached ACK
		MEMMAN_DELETE(ack);
		delete ack;
	}
	ack = create_request(ACK);
	ack->get_destination(ip_port, *user_config);

	// If for some strange reason the destination could
	// not be computed then wait for a retransmission of
	// 2XX.
	if (ip_port.ipaddr != 0 && ip_port.port != 0) {
		evq_sender->push_network(ack, ip_port);
	} else {
		log_file->write_header("t_dialog::ack_2xx_invite", LOG_SIP, LOG_CRITICAL);
		log_file->write_raw("Cannot determine destination IP address for ACK.\n\n");
		log_file->write_raw(ack->encode());
		log_file->write_footer();
	}

	remove_client_request(&req_out_invite);
}

void t_dialog::send_prack_if_required(t_response *r) {
	t_user *user_config = phone_user->get_user_profile();
	
	// RFC 3262
	// Send PRACK if needed
	if (r->get_class() == R_1XX && r->code != R_100_TRYING) {
		// RFC 3262 4
		// Send PRACK if the 1xx response is sent reliable and 100rel
		// is enabled.
		if (r->hdr_to.tag.size() > 0 &&
		    r->hdr_require.contains(EXT_100REL) &&
		    r->hdr_rseq.is_populated() &&
		    remote_target_uri.is_valid() &&
		    user_config->get_ext_100rel() != EXT_DISABLED)
		{
			t_request *prack = create_request(PRACK);
			prack->hdr_rack.set_method(r->hdr_cseq.method);
			prack->hdr_rack.set_cseq_nr(r->hdr_cseq.seqnr);
			prack->hdr_rack.set_resp_nr(r->hdr_rseq.resp_nr);

			// Delete previous PRACK request if it is still pending
			if (req_prack) {
				log_file->write_report("Previous PRACK still pending.",
					"t_dialog::send_prack_if_needed");
				remove_client_request(&req_prack);
			}

			req_prack = new t_client_request(user_config, prack, 0);
			MEMMAN_NEW(req_prack);
			line->send_request(prack, req_prack->get_tuid());
			MEMMAN_DELETE(prack);
			delete prack;
		}
	}
}

bool t_dialog::must_discard_100rel(t_response *r) {
	t_user *user_config = phone_user->get_user_profile();
	
	// RFC 3262 4
	// Discard retransmissiona and out-of-sequence reliable
	// provisional responses.
	if (r->code > R_100_TRYING && r->hdr_to.tag.size() > 0 &&
	    r->hdr_require.contains(EXT_100REL) &&
	    r->hdr_rseq.is_populated() &&
	    user_config->get_ext_100rel() != EXT_DISABLED)
	{
		if (remote_resp_nr == 0) {
			// This is the first response with a repsonse nr.
			// Initialize the remote response nr
			remote_resp_nr = r->hdr_rseq.resp_nr;
			return false;
		}
	
		if (r->hdr_rseq.resp_nr <= remote_resp_nr) {
			// This is a retransmission.
			// PRACK has already been sent. The transaction
			// layer takes care of retransmitting PRACK
			// if PRACK got lost.
			log_file->write_report("Discard 1xx retransmission.",
				"t_dialog::must_discard_100rel");
			return true;
		}

		if (r->hdr_rseq.resp_nr != remote_resp_nr + 1) {
			// A provisional response has been lost.
			// Discard this response and wait for a retransmission
			// of the lost response.
			log_file->write_report("Discard out-of-order 1xx",
				"t_dialog::must_discard_100rel");
			return true;
		}
	}

	remote_resp_nr = r->hdr_rseq.resp_nr;
	return false;
}

bool t_dialog::respond_prack(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;

	// RFC 3262 3
	if (resp_1xx_invite &&
	    r->hdr_rack.method == resp_1xx_invite->hdr_cseq.method &&
	    r->hdr_rack.cseq_nr == resp_1xx_invite->hdr_cseq.seqnr &&
	    r->hdr_rack.resp_nr == resp_1xx_invite->hdr_rseq.resp_nr)
	{
		// The provisional response has been delivered now.
		line->stop_timer(LTMR_100REL_TIMEOUT, get_object_id());
		line->stop_timer(LTMR_100REL_GUARD, get_object_id());
		MEMMAN_DELETE(resp_1xx_invite);
		delete resp_1xx_invite;
		resp_1xx_invite = NULL;

		// Send 200 on the PRACK request
		resp = r->create_response(R_200_OK);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;

		return true;
	} else {
		// PRACK does not match pending 1xx response
		// Send a 481 on the PRACK request
		resp = r->create_response(R_481_TRANSACTION_NOT_EXIST);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;

		return false;
	}
}

void t_dialog::send_request(t_request *r, t_tuid tuid) {
	line->send_request(r, tuid);
}

////////////
// Public
////////////

t_dialog::t_dialog(t_line *_line) :
	t_abstract_dialog(_line->get_phone_user())
{
	line = _line;
	
	req_out = NULL;
	req_out_invite = NULL;
	req_in_invite = NULL;
	req_cancel = NULL;
	req_prack = NULL;
	req_refer = NULL;
	req_info = NULL;
	req_stun = NULL;

	request_cancelled = false;
	end_after_ack = false;
	end_after_2xx_invite = false;
	answer_after_prack = false;
	ringing_received = false;
	
	resp_invite = NULL;
	resp_1xx_invite = NULL;
	ack = NULL;

	state = DS_NULL;

	// Timers
	dur_ack_timeout = 0;
	id_ack_timeout = 0;
	id_ack_guard = 0;
	id_re_invite_guard = 0;
	id_glare_retry = 0;
	id_cancel_guard = 0;

	// RFC 3262
	// Timers
	dur_100rel_timeout = 0;
	id_100rel_timeout = 0;
	id_100rel_guard = 0;
	
	t_user *user_config = phone_user->get_user_profile();

	// Create session
	session = new t_session(this, USER_HOST(user_config, AUTO_IP4_ADDRESS), line->get_rtp_port());
	MEMMAN_NEW(session);
	session_re_invite = NULL;

	// Subscription
	sub_refer = NULL;
	is_referred_call = false;
	refer_state = REFST_NULL;
	refer_accepted = false;
	refer_succeeded = false;
	out_refer_req_failed = false;
}

t_dialog::~t_dialog() {
	if (req_out) remove_client_request(&req_out);
	if (req_out_invite) remove_client_request(&req_out_invite);
	if (req_in_invite) remove_client_request(&req_in_invite);
	if (req_cancel) remove_client_request(&req_cancel);
	if (req_prack) remove_client_request(&req_prack);
	if (req_refer) remove_client_request(&req_refer);
	if (req_info) remove_client_request(&req_info);
	if (req_stun) remove_client_request(&req_stun);
	if (resp_invite) { MEMMAN_DELETE(resp_invite); delete resp_invite; }
	if (resp_1xx_invite) {
		MEMMAN_DELETE(resp_1xx_invite);
		delete resp_1xx_invite;
	}
	if (ack) { MEMMAN_DELETE(ack); delete ack; }
	if (session) { MEMMAN_DELETE(session); delete session; }
	if (session_re_invite) {
		MEMMAN_DELETE(session_re_invite);
		delete session_re_invite;
	}
	if (sub_refer) { MEMMAN_DELETE(sub_refer); delete sub_refer; }
	
	for (list<t_client_request *>::iterator i = inc_req_queue.begin();
	     i != inc_req_queue.end(); i++)
	{
		MEMMAN_DELETE(*i);
		delete *i;
	}
}

// Copy will only be used on the open dialog.
t_dialog *t_dialog::copy(void) {
	t_dialog *d = new t_dialog(*this);
	MEMMAN_NEW(d);

	d->generate_new_id();

	// Increment reference count on client request
	if (req_out) d->req_out->inc_ref_count();
	if (req_out_invite) d->req_out_invite->inc_ref_count();
	if (req_in_invite) d->req_in_invite->inc_ref_count();
	if (req_prack) d->req_prack->inc_ref_count();
	if (req_refer) d->req_refer->inc_ref_count();
	if (req_stun) d->req_stun->inc_ref_count();

	// The open dialog will handle the CANCEL, so delete it
	// from the copy.
	if (req_cancel) d->req_cancel = NULL;

	if (resp_invite) d->resp_invite = (t_response *)resp_invite->copy();
	if (resp_1xx_invite) d->resp_1xx_invite = (t_response *)resp_1xx_invite->copy();
	if (ack) d->ack = (t_request *)ack->copy();
	dur_ack_timeout = 0;
	id_ack_timeout = 0;
	id_ack_guard = 0;
	dur_100rel_timeout = 0;
	id_100rel_timeout = 0;
	id_100rel_guard = 0;

	if (session) {
		d->session = new t_session(*session);
		MEMMAN_NEW(d->session);
		d->session->set_owner(d);

		// If an audio session was already created for early media
		// then the audio session will be moved to the copy of the
		// dialog. Only 1 dialog can have an audio session.
		// See process_1xx_2xx_invite_resp for more information on
		// early media problems.
		// Clear a possible audio session in the open dialog.
		t_audio_session *as = session->get_audio_session();
		if (as) {
			as->set_session(d->session);
			session->set_audio_session(NULL);
			log_file->write_report(
				"An audio session was created on an open dialog.",
				"t_dialog::copy",
				LOG_NORMAL, LOG_DEBUG);
		}
	}
	
	log_file->write_header("t_dialog::copy", LOG_NORMAL, LOG_DEBUG);
	log_file->write_raw("Created dialog through copy, id=");
	log_file->write_raw(d->get_object_id());
	log_file->write_endl();
	log_file->write_footer();

	return d;
}

void t_dialog::send_invite(const t_url &to_uri, const string &to_display,
		const string &subject, const t_hdr_referred_by &hdr_referred_by,
		const t_hdr_replaces &hdr_replaces, 
		const t_hdr_require &hdr_require, 
		const t_hdr_request_disposition &hdr_request_disposition,
		bool anonymous)
{
	t_user *user_config = phone_user->get_user_profile();
	
	if (state != DS_NULL) {
		throw X_DIALOG_ALREADY_ESTABLISHED;
	}

	// If STUN is enabled, then first send a STUN binding request to
	// discover the IP adderss and port for media.
	if (phone->use_stun(user_config)) {
		if (!stun_bind_media()) {
			ui->cb_stun_failed_call_ended(line->get_line_number());
			state = DS_TERMINATED;
			return;
		}
	}
	
	t_request invite(INVITE);
	
	// RFC 3261 12.2.1.1
	// Request URI and Route header
	invite.set_route(to_uri, phone_user->get_service_route());

	// Set Call-ID header
	call_id = NEW_CALL_ID(user_config);
	invite.hdr_call_id.set_call_id(call_id);
	call_id_owner = true;

	// Set To header
	invite.hdr_to.set_uri(to_uri);
	invite.hdr_to.set_display(to_display);

	// Set From header
	local_tag = NEW_TAG;
	local_uri.set_url(line->create_user_uri());
	local_display = user_config->get_display(anonymous);
	invite.hdr_from.set_uri(local_uri);
	invite.hdr_from.set_display(local_display);
	invite.hdr_from.set_tag(local_tag);
	
	// Privacy header
	if (line->get_hide_user()) {
		invite.hdr_privacy.add_privacy(PRIVACY_ID);
	}
	
	// Set P-Preferred-Identity header
	if (anonymous && user_config->get_send_p_preferred_id()) {
		t_identity identity;
		identity.set_uri(user_config->create_user_uri(false));
		identity.set_display(user_config->get_display(false));
		invite.hdr_p_preferred_identity.add_identity(identity);
	}
	// Set P-Asserted-Identity header
	if (anonymous && user_config->get_send_p_asserted_id()) {
		t_identity identity;
		identity.set_uri(user_config->create_user_uri(false));
		identity.set_display(user_config->get_display(false));
		invite.hdr_p_asserted_identity.add_identity(identity);
	}

	// Set CSeq header
	local_seqnr = rand() % 1000 + 1;
	invite.hdr_cseq.set_method(INVITE);
	invite.hdr_cseq.set_seqnr(local_seqnr);

	// Set Max-Forwards header
	invite.hdr_max_forwards.set_max_forwards(MAX_FORWARDS);

	// User-Agent
	SET_HDR_USER_AGENT(invite.hdr_user_agent);

	// RFC 3261 13.2.1
	// Allow and Supported headers
	SET_HDR_ALLOW(invite.hdr_allow, user_config);
	SET_HDR_SUPPORTED(invite.hdr_supported, user_config);

	// Extensions specific for INVITE
	if (user_config->get_ext_100rel() != EXT_DISABLED) {
		invite.hdr_supported.add_feature(EXT_100REL);
	}

	// Require header
	switch (user_config->get_ext_100rel()) {
	case EXT_PREFERRED:
	case EXT_REQUIRED:
		invite.hdr_require.add_feature(EXT_100REL);
		break;
	default:
		break;
	}

	// Subject header
	if (subject != "") {
		invite.hdr_subject.set_subject(subject);
	}

	// Organization
	if (!anonymous) {
		SET_HDR_ORGANIZATION(invite.hdr_organization, user_config);
	}

	// RFC 3892 Referred-By header if a call is initated because
	// of an incoming REFER.
	invite.hdr_referred_by = hdr_referred_by;
	
	// RFC 3891 Replaces header
	invite.hdr_replaces = hdr_replaces;
	
	// Add required extension passed by the upper layer
	if (hdr_require.is_populated()) {
		invite.hdr_require.add_features(hdr_require.features);
	}
	
	// RFC 3841 Request-Disposition header
	invite.hdr_request_disposition = hdr_request_disposition;
	
	// Calculate destinations
	// See create_request() for more comments
	invite.calc_destinations(*user_config);
	
        // The Contatc, Via header and SDP can only be created after the destinations
        // are calculated, because the destination deterimines which
        // local IP address should be used.
        
	// Create SDP offer
	session->create_sdp_offer(&invite, SDP_O_USER);
	
	// Set Via header
	unsigned long local_ip = invite.get_local_ip();
	t_via via(USER_HOST(user_config, h_ip2str(local_ip)), PUBLIC_SIP_PORT(user_config));
	invite.hdr_via.add_via(via);
	
	// Set Contact header
	t_contact_param contact;
	contact.uri.set_url(line->create_user_contact(h_ip2str(local_ip)));
	invite.hdr_contact.add_contact(contact);
	
	// Send INVITE
	req_out_invite = new t_client_request(user_config, &invite, 0);
	MEMMAN_NEW(req_out_invite);
	
	// Trigger call script
	t_call_script script(user_config, t_call_script::TRIGGER_OUT_CALL,
			line->get_line_number() + 1);
	script.exec_notify(&invite);
	
	line->send_request(&invite, req_out_invite->get_tuid());
	line->call_hist_record.start_call(&invite, t_call_record::DIR_OUT, 
		user_config->get_profile_name());

	state = DS_W4INVITE_RESP;
}

bool t_dialog::resend_invite_auth(t_response *resp) {
	t_user *user_config = phone_user->get_user_profile();
	if (!req_out_invite) return false;

	assert(state == DS_W4INVITE_RESP || state == DS_W4INVITE_RESP2);

	t_request *req = req_out_invite->get_request();

	// Add authorization header, increment CSeq and create new branch id
	if (get_phone()->authorize(user_config, req, resp)) {
		resend_request(req_out_invite);

		// Reset state in case a 100 Trying was received
		state = DS_W4INVITE_RESP;

		return true;
	}

	return false;
}

bool t_dialog::resend_invite_unsupported(t_response *resp) {
	t_user *user_config = phone_user->get_user_profile();
	
	if (!req_out_invite) return false;
	if (resp->code != R_420_BAD_EXTENSION) return false;
	if (!resp->hdr_unsupported.is_populated()) return false;
	if (resp->hdr_unsupported.features.empty()) return false;

	t_request *req = req_out_invite->get_request();

	// If no extensions were required then return.
	if (!req->hdr_require.is_populated()) return false;
	if (req->hdr_require.features.empty()) return false;
	
	bool removed_ext = false;

	for (list<string>::iterator i = resp->hdr_unsupported.features.begin();
	     i != resp->hdr_unsupported.features.end(); i++)
	{
		if (req->hdr_require.contains(*i)) {
			if (*i == EXT_100REL) {
				if (user_config->get_ext_100rel() == EXT_PREFERRED) {
					req->hdr_require.del_feature(*i);
				} else {
					// The 100rel is required.
					return false;
				}
			} else {
				// There is no specific requirement for
				// this extension so do not remove it.
				return false;
			}

			removed_ext = true;
		}
	}

	// Return if none of the unsupported extensions was required.
	if (!removed_ext) return false;

	if (req->hdr_require.features.empty()) {
		// There are no required features anymore
		req->hdr_require.unpopulate();
	}

	resend_request(req_out_invite);

	// Reset state in case a 100 Trying was received
	state = DS_W4INVITE_RESP;

	return true;
}

bool t_dialog::redirect_invite(t_response *resp) {
	t_contact_param contact;
	t_user *user_config = phone_user->get_user_profile();

	if (!req_out_invite) return false;

	// If the response is a 3XX response then add redirection contacts
	if (resp->get_class() == R_3XX  && resp->hdr_contact.is_populated()) {
		req_out_invite->redirector.add_contacts(
					resp->hdr_contact.contact_list);
	}

	// Get next destination
	if (!req_out_invite->redirector.get_next_contact(contact)) {
		// There is no next destination
		return false;
	}

	assert(state == DS_W4INVITE_RESP || state == DS_W4INVITE_RESP2);

	t_request *req = req_out_invite->get_request();

	// Ask user for permission to redirect if indicated by user config
	if (user_config->get_ask_user_to_redirect()) {
		if(!ui->cb_ask_user_to_redirect_invite(user_config,
				contact.uri, contact.display)) 
		{
			// User did not permit to redirect
			return false;
		}
	}

	// Change the request URI to the new URI.
	// As the URI changes the destination set must be recalculated
	req->uri = contact.uri;
	req->calc_destinations(*user_config);

	ui->cb_redirecting_request(user_config, line->get_line_number(), contact);
	resend_request(req_out_invite);

	// Reset state in case a 100 Trying was received
	state = DS_W4INVITE_RESP;

	return true;
}

bool t_dialog::failover_invite(void) {
	if (!req_out_invite) return false;
	
	log_file->write_report("Failover to next destination.",
				"t_dialog::failover_invite");
	
	t_request *req = req_out_invite->get_request();
	
	// Get next destination
	if (!req->next_destination()) {
		log_file->write_report("No next destination for failover.",
				"t_dialog::failover_invite");
		return false;
	}
	
	assert(state == DS_W4INVITE_RESP || state == DS_W4INVITE_RESP2);
	resend_request(req_out_invite);

	// Reset state in case a 100 Trying was received
	state = DS_W4INVITE_RESP;

	return true;
}

void t_dialog::send_bye(void) {
	t_user *user_config = phone_user->get_user_profile();
	
	switch (state) {
	case DS_W4INVITE_RESP2:
	case DS_EARLY:
	case DS_CONFIRMED:
		break;
	case DS_W4RE_INVITE_RESP:
	case DS_W4RE_INVITE_RESP2:
		// send BYE after completion of re-INVITE
		request_cancelled = true;
		return;
	case DS_W4ACK:
	case DS_W4ACK_RE_INVITE:
		// send BYE after completion of re-INVITE
		request_cancelled = true;
		return;
	case DS_TERMINATED:
		// Dialog has already been terminated. Do not send BYE.
		return;
	default:
		log_file->write_header("t_dialog::failover_invite",
			LOG_NORMAL, LOG_WARNING);
		log_file->write_raw("Cannot send BYE on dialog in state ");
		log_file->write_raw(state);
		log_file->write_endl();
		log_file->write_footer();
		return;
	}

	// If a previous request is still pending then remove it.
	if (req_out) { MEMMAN_DELETE(req_out); delete (req_out); }

	t_request *bye = create_request(BYE);
	req_out = new t_client_request(user_config, bye, 0);
	MEMMAN_NEW(req_out);
	
	// Trigger call script
	t_call_script script(user_config, t_call_script::TRIGGER_LOCAL_RELEASE,
			line->get_line_number() + 1);
	script.exec_notify(bye);
	
	line->send_request(bye, req_out->get_tuid());
	line->call_hist_record.end_call(false);	
	MEMMAN_DELETE(bye);
	delete bye;

	state = DS_W4BYE_RESP;
	ui->cb_call_ended(line->get_line_number());
}

void t_dialog::send_options(void) {
	t_user *user_config = phone_user->get_user_profile();
	
	// Request can only be sent in a confirmed dialog.
	if (state != DS_CONFIRMED) return;

	// If a previous request is still pending then remove it.
	if (req_out) { MEMMAN_DELETE(req_out); delete (req_out); }

	t_request *r = create_request(OPTIONS);

	// Accept
	r->hdr_accept.add_media(t_media("application","sdp"));
	req_out = new t_client_request(user_config, r, 0);
	MEMMAN_NEW(req_out);
	line->send_request(r, req_out->get_tuid());
	MEMMAN_DELETE(r);
	delete r;
}

void t_dialog::send_cancel(bool early_dialog_exists) {
	t_request *cancel;
	t_user *user_config = phone_user->get_user_profile();

	switch (state) {
	case DS_W4INVITE_RESP:
	case DS_W4RE_INVITE_RESP:
		if (!early_dialog_exists) {
			// wait for first response then send CANCEL or BYE
			request_cancelled = true;
			break;
		}
		// Fall through
	case DS_W4INVITE_RESP2:
	case DS_W4RE_INVITE_RESP2:
	case DS_EARLY:
		if (req_cancel) {
			// CANCEL has been sent already
			break;
		}
		
		cancel = create_request(CANCEL);
		req_cancel = new t_client_request(user_config, cancel, 0);
		MEMMAN_NEW(req_cancel);
		line->send_request(cancel, req_cancel->get_tuid());
		MEMMAN_DELETE(cancel);
		delete cancel;
		
		// Make sure dialog is terminated if CANCEL glares with
		// 2XX on INVITE.
		set_end_after_2xx_invite(true);
		break;
	default:
		break;
	}
	
	ui->cb_call_ended(line->get_line_number());
}

void t_dialog::set_end_after_2xx_invite(bool on) {
	end_after_2xx_invite = on;
}

void t_dialog::send_re_invite(void) {
	assert(session_re_invite);
	t_user *user_config = phone_user->get_user_profile();

	// Request can only be sent in a confirmed dialog.
	if (state != DS_CONFIRMED) return;

	// Do nothing if a re-INVITE is already in progress
	if (req_out_invite) return;

	t_request *r = create_request(INVITE);

	// Set Contact header
	// INVITE must contain a contact header
	t_contact_param contact;
	contact.uri.set_url(line->create_user_contact(h_ip2str(r->get_local_ip())));
	r->hdr_contact.add_contact(contact);

	// RFC 3261 13.2.1
	// Allow and Supported headers
	SET_HDR_ALLOW(r->hdr_allow, user_config);
	SET_HDR_SUPPORTED(r->hdr_supported, user_config);

	// Extensions specific for INVITE
	if (user_config->get_ext_100rel() != EXT_DISABLED) {
		// If some weird far end implementation wants to send
		// a reliable provisional then support it.
		// As a provisional response not needed for a re-INVITE,
		// do not require the 100rel.
		r->hdr_supported.add_feature(EXT_100REL);
	}

	// Create SDP offer
	session_re_invite->create_sdp_offer(r, SDP_O_USER);

	// Send INVITE
	req_out_invite = new t_client_request(user_config, r, 0);
	MEMMAN_NEW(req_out_invite);
	line->send_request(r, req_out_invite->get_tuid());
	MEMMAN_DELETE(r);
	delete r;

	state = DS_W4RE_INVITE_RESP;
}

bool t_dialog::resend_request_auth(t_response *resp) {
	t_client_request **current_cr;

	switch (resp->hdr_cseq.method) {
	case INVITE:
		// re-INVITE
		if (!req_out_invite) return false;
		assert(state == DS_W4RE_INVITE_RESP ||
		       state == DS_W4RE_INVITE_RESP2);
		current_cr = &req_out_invite;
		break;
	case PRACK:
		if (!req_prack) return false;
		current_cr = &req_prack;
		break;
	case REFER:
		if (!req_refer) return false;
		current_cr = &req_refer;
		break;
	case INFO:
		if (!req_info) return false;
		current_cr = &req_info;
		break;
	case SUBSCRIBE:
	case NOTIFY:
		if (!sub_refer) return false;
		if (!sub_refer->req_out) return false;
		current_cr = &(sub_refer->req_out);
		break;
	default:
		// other requests
		if (!req_out) return false;
		current_cr = &req_out;
	}
	
	if (t_abstract_dialog::resend_request_auth(*current_cr, resp)) {
		if (resp->hdr_cseq.method == INVITE) {
			// Reset state in case a 100 Trying was received
			state = DS_W4RE_INVITE_RESP;
		}
		return true;
	}

	return false;
}

bool t_dialog::redirect_request(t_response *resp) {
	t_client_request **current_cr;
	t_user *user_config = phone_user->get_user_profile();
	
	if (resp->hdr_cseq.method == INVITE) {
		// re-INVITE
		if (!req_out_invite) return false;
		assert(state == DS_W4RE_INVITE_RESP ||
		       state == DS_W4RE_INVITE_RESP2);
		current_cr = &req_out_invite;
	} else {
		// non-INVITE
		if (!req_out) return false;
		current_cr = &req_out;
	}
	
	t_contact_param contact;
	if (!t_abstract_dialog::redirect_request(*current_cr, resp, contact)) return false;

	// Re-INVITE
	if (resp->hdr_cseq.method == INVITE) {
		// Reset state in case a 100 Trying was received
		state = DS_W4RE_INVITE_RESP;
	}
	
	ui->cb_redirecting_request(user_config, line->get_line_number(), contact);
	return true;
}

bool t_dialog::failover_request(t_response *resp) {
	t_client_request **current_cr;

	if (resp->hdr_cseq.method == INVITE) {
		// re-INVITE
		if (!req_out_invite) return false;
		assert(state == DS_W4RE_INVITE_RESP ||
		       state == DS_W4RE_INVITE_RESP2);
		current_cr = &req_out_invite;
	} else {
		// non-INVITE
		if (!req_out) return false;
		current_cr = &req_out;
	}
	
	if (!t_abstract_dialog::failover_request(*current_cr)) return false;

	// Re-INVITE
	if (resp->hdr_cseq.method == INVITE) {
		// Reset state in case a 100 Trying was received
		state = DS_W4RE_INVITE_RESP;
	}

	return true;	
}

void t_dialog::hold(bool rtponly) {
	assert(!session_re_invite);

	// Stop glare retry timer
	if (id_glare_retry) {
		line->stop_timer(LTMR_GLARE_RETRY, get_object_id());
	}

	reinvite_purpose = REINVITE_HOLD;

	if (rtponly) {
		session->stop_rtp();
		session->hold();

		// Stopping the RTP only is like a full call hold where
		// the re-INVITE failed. By setting the hold_failed flag,
		// a subsequent retrieve will only start RTP.
		hold_failed = true;
		return;
	}

	hold_failed = false;
	session_re_invite = session->create_call_hold();
	send_re_invite();

	// Stop the audio streams now. If we do not stop the stream now
	// the stream will be stopped when a 200 OK is received on the
	// re-INVITE. However, when the line is put on-hold because
	// the user switches to another line that already has a held call
	// a race condition might occur:
	//
	// 1. A re-INVITE on this line is sent to put it on-hold
	// 2. A re-INVITE on the other line is sent to retrieve the call
	// 3. If the 200 OK on the second re-INVITE comes in before the
	//    the 200 OK on the first re-INVITE, then the audio streams
	//    for the second line will be started already while the first
	//    line still has the audio device open. On some systems this
	//    causes a dead lock as the audio device may only be opened
	//    once.
	//
	// Also if the re-INVITE to put the line on-hold fails, the
	// audio might not be stopped at all. It must be stopped however
	// as the user has switched to the other line. So stopping the
	// audio now will make sure the audio device is idle when the
	// second call is retrieved.
	session->stop_rtp();

	// Prevent RTP stream from getting started even if the signaling 
	// for hold fails. After all the user has put the phone locally
	// on-hold, so RTP should never be started.
	session->hold();
}

void t_dialog::retrieve(void) {
	assert(!session_re_invite);
	t_user *user_config = phone_user->get_user_profile();

	// Stop glare retry timer
	if (id_glare_retry) {
		line->stop_timer(LTMR_GLARE_RETRY, get_object_id());
	}
	
	// Allow RTP stream to be started again.
	session->unhold();

	// If the previous call-hold failed, then only RTP needs to
	// be restarted. The session description did never change
	// because of the failure.
	if (hold_failed) {
		session->start_rtp();
		return;
	}
	
	// If STUN is enabled, then first send a STUN binding request to
	// discover the IP adderss and port for media.
	if (phone->use_stun(user_config)) {
		if (!stun_bind_media()) {	
			// No re-INVITE can be sent. Simply return.
			// User will decide if the call should be
			// torn down.
			return;
		}
	}

	reinvite_purpose = REINVITE_RETRIEVE;
	session_re_invite = session->create_call_retrieve();
	send_re_invite();
}

void t_dialog::kill_rtp(void){
	session->kill_rtp();
	if (session_re_invite) session_re_invite->kill_rtp();
}

void t_dialog::send_refer(const t_url &uri, const string &display) {
	t_user *user_config = phone_user->get_user_profile();
	
	if (state != DS_CONFIRMED) return;

	if (refer_state != REFST_NULL) return;
	
	// If a previous refer is still in progress, then do nothing
	if (req_refer) {
		log_file->write_report("A REFER request is already in progress.",
			"t_dialog::send_refer");
		return;
	}

	// If a refer subscription already exists, then do nothing
	if (sub_refer) {
		log_file->write_report("Refer subscription exists already.",
			"t_dialog::send_refer");
		return;
	}

	t_request *refer = create_request(REFER);

	// Refer-To header
	refer->hdr_refer_to.set_uri(uri);
	refer->hdr_refer_to.set_display(display);

	// Referred-By header
	refer->hdr_referred_by.set_uri(line->create_user_uri());
	refer->hdr_referred_by.set_display(user_config->get_display(line->get_hide_user()));

	req_refer = new t_client_request(user_config, refer, 0);
	MEMMAN_NEW(req_refer);
	line->send_request(refer, req_refer->get_tuid());
	MEMMAN_DELETE(refer);
	delete refer;

	refer_succeeded = false;
	out_refer_req_failed = false;
	refer_state = REFST_W4RESP;
}

void t_dialog::send_dtmf(char digit, bool inband, bool info) {
	t_user *user_config = phone_user->get_user_profile();
	
	if (info) {
		if (req_info) {
			// An INFO request is still in progress, put the
			// DTMF digit in the queue
			dtmf_queue.push(digit);
		} else {
			t_request *info_request = create_request(INFO);
			
			// Content-Type header
			info_request->hdr_content_type.set_media(t_media("application", "dtmf-relay"));
			
			// application/dtmf-relay body
			info_request->body = new t_sip_body_dtmf_relay(digit,
					user_config->get_dtmf_duration());
			MEMMAN_NEW(info_request->body);
			
			req_info = new t_client_request(user_config, info_request, 0);
			MEMMAN_NEW(req_info);
			line->send_request(info_request, req_info->get_tuid());
			MEMMAN_DELETE(info_request);
			delete info_request;
			
			ui->cb_send_dtmf(line->get_line_number(), char2dtmf_ev(digit));
		}
	} else {
		if (session) session->send_dtmf(digit, inband);
	}
}

bool t_dialog::stun_bind_media(void) {
	t_user *user_config = phone_user->get_user_profile();
	
	try {
		unsigned long mapped_ip;
		unsigned short mapped_port;
		int stun_err_code;
		string stun_err_reason;
		bool ret = get_stun_binding(user_config, line->get_rtp_port(),
			mapped_ip, mapped_port,
			stun_err_code, stun_err_reason);
			
		if (!ret) {
			// STUN request failed
			ui->cb_stun_failed(user_config, stun_err_code, stun_err_reason);
			
			log_file->write_header("t_dialog::stun_bind_media", 
				LOG_NORMAL, LOG_CRITICAL);
			log_file->write_raw("STUN bind request for media failed.\n");
			log_file->write_raw(stun_err_code);
			log_file->write_raw(" ");
			log_file->write_raw(stun_err_reason);
			log_file->write_endl();
			log_file->write_footer();
			return false;
		}
		
		// STUN binding request succeeded.
		session->receive_host = h_ip2str(mapped_ip);
		session->receive_port = mapped_port;
	} catch (int err) {
		// STUN request failed
		ui->cb_stun_failed(user_config);
		
		log_file->write_header("t_dialog::stun_bind_media", 
			LOG_NORMAL, LOG_CRITICAL);
		log_file->write_raw("STUN bind request for media failed.\n");
		log_file->write_raw(get_error_str(err));
		log_file->write_endl();
		log_file->write_footer();
		return false;
	}
	
	return true;
}

void t_dialog::recvd_response(t_response *r, t_tuid tuid, t_tid tid) {
	t_user *user_config = phone_user->get_user_profile();
	t_abstract_dialog::recvd_response(r, tuid, tid);

	if (r->hdr_cseq.method == INVITE &&
	    tuid == 0 && tid == 0 && !req_out_invite)
	{
		t_ip_port ip_port;

		// Only a retransmission of a 2XX INVITE is allowed.
		if (r->get_class() != R_2XX) return;
		if (!ack) return;
		if (r->hdr_cseq.seqnr != ack->hdr_cseq.seqnr)
		{
			// The 2XX response does not match the ACK
			return;
		}

		ack->get_destination(ip_port, *user_config);
		if (ip_port.ipaddr != 0 && ip_port.port != 0) {
			evq_sender->push_network(ack, ip_port);
		}

		return;
	}

	if (r->hdr_cseq.method == CANCEL) {
		if (!req_cancel) return;
		if (r->is_final()) {
			remove_client_request(&req_cancel);
			if (r->is_success()) {
				line->start_timer(LTMR_CANCEL_GUARD, get_object_id());
			} else {
				// CANCEL request failed.
				ui->cb_cancel_failed(line->get_line_number(), r);

				// Abort the INVITE as the user cannot terminate
				// it in a normal way.
				if (req_out_invite) {
					t_tid _tid = req_out_invite->get_tid();
					if (_tid > 0) {
						evq_trans_mgr->push_abort_trans(_tid);
					}
				}
			}
		}
		return;
	}

	// No processing done for PRACK responses.
	if (r->hdr_cseq.method == PRACK) {
		if (!req_prack) return;
		t_request *prack = req_prack->get_request();

		if (r->hdr_cseq.seqnr != prack->hdr_cseq.seqnr) {
			// The response does not match the latest sent PRACK.
			// It might match a previous sent PRACK. However, when
			// a previous PRACK fails, then the latest PRACK will also
			// fail, so the failure will be handled in the end without
			// the overhead to keep a list of all pending PRACKs which
			// should be a rare case.
			return;
		}

		if (r->is_final()) {
			// PRACK is finished, so remove request
			remove_client_request(&req_prack);

			// Tear down the call if PRACK failed and call is
			// not yet established.
			if (!r->is_success() && state == DS_EARLY) {
				log_file->write_header("t_dialog::recvd_response",
					LOG_NORMAL, LOG_WARNING);
				log_file->write_raw("PRACK failed: ");
				log_file->write_raw(r->code);
				log_file->write_raw(" ");
				log_file->write_raw(r->reason);
				log_file->write_endl();
				log_file->write_raw("Call will be cancelled.\n");
				log_file->write_footer();

				ui->cb_prack_failed(line->get_line_number(), r);
				send_cancel(true);

				// Ignore the failure in other states.
				// The call has been setup, so all seems fine.
			}
		}

		return;
	}

	// Determine if this is an INVITE or non-INVITE response
	t_client_request *req;
	bool send_to_sub_refer = false;

	switch(r->hdr_cseq.method) {
	case INVITE:
		req = req_out_invite;
		break;
	case SUBSCRIBE:
	case NOTIFY:
		if (!sub_refer) return;
		req = sub_refer->req_out;
		send_to_sub_refer = true;
		break;
	case REFER:
		req = req_refer;
		break;
	case INFO:
		req = req_info;
		break;
	default:
		req = req_out;
	}

	// Discard response if no request is pending
	if (!req) {
		return;
	}

	// Check cseq
	if (r->hdr_cseq.method != req->get_request()->method) {
		return;
	}
	if (r->hdr_cseq.seqnr != req->get_request()->hdr_cseq.seqnr) return;

	// Set the transaction identifier. This identifier is needed if the
	// transaction must be aborted at a later time.
	req->set_tid(tid);

	if (send_to_sub_refer) {
		sub_refer->recv_response(r, tuid, tid);
		if (sub_refer->get_state() == SS_TERMINATED) {
			MEMMAN_DELETE(sub_refer);
			delete sub_refer;
			sub_refer = NULL;
			if (state == DS_CONFIRMED_SUB) {
				state = DS_TERMINATED;
			}
		}
		return;
	}

	switch (state) {
	case DS_W4INVITE_RESP:
	case DS_W4INVITE_RESP2:
		state_w4invite_resp(r, tuid, tid);
		break;
	case DS_EARLY:
		state_early(r, tuid, tid);
		break;
	case DS_W4BYE_RESP:
		state_w4bye_resp(r, tuid, tid);
		break;
	case DS_CONFIRMED:
		state_confirmed_resp(r, tuid, tid);
		break;
	case DS_W4RE_INVITE_RESP:
	case DS_W4RE_INVITE_RESP2:
		state_w4re_invite_resp(r, tuid, tid);
		break;
	default:
		// No response expected in other states. Discard.
		break;
	}
}

void t_dialog::recvd_request(t_request *r, t_tuid tuid, t_tid tid) {
	t_response *resp;
	t_user *user_config = phone_user->get_user_profile();

	// CANCEL will be handled by recvd_cancel()
	
	t_abstract_dialog::recvd_request(r, tuid, tid);

	switch (r->method) {
	case ACK:
		// When ACK is received then the current incoming request
		// must be INVITE.
		if (!req_in_invite) return;
		if (req_in_invite->get_request()->hdr_cseq.seqnr !=
				r->hdr_cseq.seqnr)
		{
			log_file->write_header("t_dialog::recvd_request",
					LOG_NORMAL, LOG_WARNING);
			log_file->write_raw("ACK does not match a pending INVITE.\n");
			log_file->write_raw("Discard ACK.\n");
			log_file->write_footer();
			return;
		}
		break;
	case INVITE:
		if (remote_seqnr_set && r->hdr_cseq.seqnr <= remote_seqnr) {
			// Request received out of sequence. Discard.
			log_file->write_header("t_dialog::recvd_request",
					LOG_NORMAL, LOG_WARNING);
			log_file->write_raw("INVITE is received out of order.\n");
			log_file->write_raw("Remote seqnr = ");
			log_file->write_raw(remote_seqnr);
			log_file->write_endl();
			log_file->write_raw("Received seqnr = ");
			log_file->write_raw(r->hdr_cseq.seqnr);
			log_file->write_endl();
			log_file->write_raw("Discard INVITE.\n");
			log_file->write_footer();
			return;
		}
		
		remote_seqnr = r->hdr_cseq.seqnr;
		remote_seqnr_set = true;

		if (req_in_invite) {
			// RFC 3261 14.2
			// Another INVITE is received while the previous
			// one is not finished.
			resp = r->create_response(R_500_INTERNAL_SERVER_ERROR,
				"Previous INVITE still in progress");
			line->send_response(resp, tuid, tid);
			MEMMAN_DELETE(resp);
			delete resp;
			return;
		} else if (req_out_invite) {
			// RFC 3261 14.2
			// re-INVITE glare
			resp = r->create_response(R_491_REQUEST_PENDING);
			line->send_response(resp, tuid, tid);
			MEMMAN_DELETE(resp);
			delete resp;
			return;
		} else {
			req_in_invite = new t_client_request(user_config, r, tid);
			MEMMAN_NEW(req_in_invite);
		}
		break;
	case REFER:
		// Reset refer_accepted indication.
		refer_accepted = false;
		// fall thru
	default:
		// Check cseq
		// RFC 3261 12.2.2
		if (remote_seqnr_set && r->hdr_cseq.seqnr <= remote_seqnr) {
			// Request received out of order.		
			log_file->write_header("t_dialog::recvd_request",
				LOG_NORMAL, LOG_WARNING);
			log_file->write_raw("CSeq seqnr is out of sequence.\n");
			log_file->write_raw("Reveived seqnr: ");
			log_file->write_raw(r->hdr_cseq.seqnr);
			log_file->write_endl();
			log_file->write_raw("Remote seqnr: ");
			log_file->write_raw(remote_seqnr);
			log_file->write_endl();
			log_file->write_footer();
			
			resp = r->create_response(R_500_INTERNAL_SERVER_ERROR,
				"Request received out of order");
			line->send_response(resp, tuid, tid);
			MEMMAN_DELETE(resp);
			delete resp;

			return;
		}
		
		remote_seqnr = r->hdr_cseq.seqnr;
		remote_seqnr_set = true;
	}

	t_dialog_state old_state = state;
	
	switch (state) {
	case DS_NULL:
		state_null(r, tuid, tid);
		break;
	case DS_W4ACK:
		state_w4ack(r, tuid, tid);
		break;
	case DS_W4ACK_RE_INVITE:
		state_w4ack_re_invite(r, tuid, tid);
		break;
	case DS_W4ANSWER:
		state_w4answer(r, tuid, tid);
		break;
	case DS_W4RE_INVITE_RESP:
	case DS_W4RE_INVITE_RESP2:
		state_w4re_invite_resp(r, tuid, tid);
		break;
	case DS_W4BYE_RESP:
		state_w4bye_resp(r, tuid, tid);
		break;
	case DS_CONFIRMED:
		state_confirmed(r, tuid, tid);
		break;
	case DS_CONFIRMED_SUB:
		state_confirmed_sub(r, tuid, tid);
		break;
	default:
		// No request expected in other states. Discard.
		resp = r->create_response(R_500_INTERNAL_SERVER_ERROR);
		line->send_response(resp, tuid, tid);
		MEMMAN_DELETE(resp);
		delete resp;
		break;
	}
	
	// If the state has changed, then waiting requests needs to be
	// processed.
	if (state != old_state && !inc_req_queue.empty()) {
		t_client_request *queued_cr = inc_req_queue.front();
		inc_req_queue.pop_front();
		
		log_file->write_header("t_dialog::recvd_request", 
				LOG_NORMAL, LOG_INFO);
		log_file->write_raw("Process queued ");
		log_file->write_raw(method2str(r->method, r->unknown_method));
		log_file->write_endl();
		log_file->write_footer();

		recvd_request(queued_cr->get_request(), 0, queued_cr->get_tid());
		MEMMAN_DELETE(queued_cr);
		delete queued_cr;
	}
}

// RFC 3261 9.2
void t_dialog::recvd_cancel(t_request *r, t_tid cancel_tid,
		t_tid target_tid)
{
	t_response *resp;

	assert(r->method == CANCEL);

	// Send 200 as response to CANCEL
	resp = r->create_response(R_200_OK);
	// RFC 3261 9.2
	// The To-tag in the response to the CANCEL should be the same
	// as the To-tag in the original request.
	resp->hdr_to.set_tag(local_tag);
	line->send_response(resp, 0, cancel_tid);
	MEMMAN_DELETE(resp);
	delete resp;

	switch (state) {
	case DS_W4ANSWER:
		state_w4answer(r, 0, cancel_tid);
		break;
	default:
		// Ignore CANCEL in other states.
		break;
	}
}

void t_dialog::recvd_stun_resp(StunMessage *r, t_tuid tuid, t_tid tid) {
	// Not used anymore.
	// STUN requests are performed in a synchronous way.
}

// RFC 3261 13.3.1.4
void t_dialog::answer(void) {
	t_user *user_config = phone_user->get_user_profile();
	if (!req_in_invite) return;

	t_request *invite_req = req_in_invite->get_request();

	// RFC 3262 3
	// Delay the final response if we are still waiting for a PRACK
	// on a 1xx response containing SDP
	if (resp_1xx_invite && resp_1xx_invite->body) {
		answer_after_prack = true;
		return;
	}

	if (state != DS_W4ANSWER) {
		throw X_WRONG_STATE;
	}
	
	resp_invite = invite_req->create_response(R_200_OK);
	resp_invite->hdr_to.set_tag(local_tag);

	// Set Organization header
	SET_HDR_ORGANIZATION(resp_invite->hdr_organization, user_config);

	// RFC 3261 12.1.1
	// Copy the Record-Route header from request to response
	if (invite_req->hdr_record_route.is_populated()) {
		resp_invite->hdr_record_route = invite_req->hdr_record_route;
	}

	// Set Contact header
	t_contact_param contact;
	contact.uri.set_url(line->create_user_contact(h_ip2str(resp_invite->get_local_ip())));
	resp_invite->hdr_contact.add_contact(contact);

	// Set Allow and Supported headers
	SET_HDR_ALLOW(resp_invite->hdr_allow, user_config);
	SET_HDR_SUPPORTED(resp_invite->hdr_supported, user_config);

	// RFC 3261 13.3.1.4
	// Create SDP offer if no offer was received in INVITE and no offer
	// was sent in a reliable 1xx response (RFC 3262 5)
	// Otherwise if no offer was sent in a reliable 1xx, create an SDP answer.
	if (!session->sent_offer) {
		if (!session->recvd_offer && !session->sent_offer) {
			session->create_sdp_offer(resp_invite, SDP_O_USER);
		} else {
			session->create_sdp_answer(resp_invite, SDP_O_USER);
			session->start_rtp();
		}
	}
	
	// Trigger call script
	t_call_script script(user_config, t_call_script::TRIGGER_IN_CALL_ANSWERED,
			line->get_line_number() + 1);
	script.exec_notify(resp_invite);

	line->call_hist_record.answer_call(resp_invite);
	line->send_response(resp_invite, req_in_invite->get_tuid(),
					req_in_invite->get_tid());
	line->start_timer(LTMR_ACK_GUARD, get_object_id());
	line->start_timer(LTMR_ACK_TIMEOUT, get_object_id());

	// Stop 100rel timers if they are running.
	line->stop_timer(LTMR_100REL_GUARD, get_object_id());
	line->stop_timer(LTMR_100REL_TIMEOUT, get_object_id());

	state = DS_W4ACK;
}

void t_dialog::reject(int code, string reason) {
	t_response *resp;
	t_user *user_config = phone_user->get_user_profile();

	if (state != DS_W4ANSWER) {
		throw X_WRONG_STATE;
	}

	assert(req_in_invite);
	assert(code >= 400);

	resp = req_in_invite->get_request()->create_response(code, reason);
	resp->hdr_to.set_tag(local_tag);
	
	// Trigger call script
	t_call_script script(user_config, t_call_script::TRIGGER_IN_CALL_FAILED,
			line->get_line_number() + 1);
	script.exec_notify(resp);
		
	line->send_response(resp, req_in_invite->get_tuid(),
						req_in_invite->get_tid());
	line->call_hist_record.fail_call(resp);
	MEMMAN_DELETE(resp);
	delete resp;

	// Stop 100rel timers if they are running.
	line->stop_timer(LTMR_100REL_GUARD, get_object_id());
	line->stop_timer(LTMR_100REL_TIMEOUT, get_object_id());

	state = DS_TERMINATED;
}

void t_dialog::redirect(const list<t_display_url> &destinations, int code, string reason)
{
	t_response *resp;
	t_user *user_config = phone_user->get_user_profile();

	if (state != DS_W4ANSWER) {
		throw X_WRONG_STATE;
	}

	assert(req_in_invite);
	assert(code >= 300 && code <= 399);

	resp = req_in_invite->get_request()->create_response(code, reason);
	resp->hdr_to.set_tag(local_tag);

	t_contact_param *contact;
	float q = 0.9;
	for (list<t_display_url>::const_iterator i = destinations.begin();
	     i != destinations.end(); i++)
	{
		contact = new t_contact_param();
		MEMMAN_NEW(contact);
		contact->display = i->display;
		contact->uri = i->url;
		contact->set_qvalue(q);
		resp->hdr_contact.add_contact(*contact);
		MEMMAN_DELETE(contact);
		delete contact;
		q = q - 0.1;
		if (q < 0.1) q = 0.1;
	}
	
	// Trigger call script
	t_call_script script(user_config, t_call_script::TRIGGER_IN_CALL_FAILED,
			line->get_line_number() + 1);
	script.exec_notify(resp);

	line->send_response(resp, req_in_invite->get_tuid(),
						req_in_invite->get_tid());
	line->call_hist_record.fail_call(resp);
	MEMMAN_DELETE(resp);
	delete resp;

	// Stop 100rel timers if they are running.
	line->stop_timer(LTMR_100REL_GUARD, get_object_id());
	line->stop_timer(LTMR_100REL_TIMEOUT, get_object_id());

	state = DS_TERMINATED;
}

bool t_dialog::match_response(t_response *r, t_tuid tuid) {
	if (tuid != 0) {
  		if (req_out && req_out->get_tuid() == tuid) return true;
		if (req_out_invite && req_out_invite->get_tuid() == tuid) {
			return true;
		}
		if (req_cancel && req_cancel->get_tuid() == tuid) {
			return true;
		}
		return false;
	}
	
	// The implementation sends CANCEL on the open dialog.
	// The tags of a CANCEL response will be identical to the tags of
	// the INVITE, so it matches all pending dialogs as well.
	// So a CANCEL should only match if the dialog has a CANCEL request
	// pending.
	if (r->hdr_cseq.method == CANCEL && !req_cancel) return false;

	return t_abstract_dialog::match_response(r, tuid);
}

bool t_dialog::match_response(StunMessage *r, t_tuid tuid) {
	if (tuid == 0) return false;
	if (!req_stun) return false;

	return (req_stun->get_tuid() == tuid);
}

bool t_dialog::match_cancel(t_request *r, t_tid target_tid) {
	return (req_in_invite && req_in_invite->get_tid() == target_tid);
}

bool t_dialog::is_invite_retrans(t_request *r) {
	assert(r->method == INVITE);

	// An INVITE can only be a retransmission if an incoming INVITE is
	// still in progress.
	if (!req_in_invite) return false;
	t_request *request = req_in_invite->get_request();

	// RFC 3261 17.2.3
	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;
		return (request->hdr_cseq.method == r->hdr_cseq.method);
	}

	// Matching rules for backward compatibiliy with RFC 2543
	// TODO: verify rules for matching via headers
	return (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);
}

void t_dialog::process_invite_retrans(void) {
	t_ip_port ip_port;
	
	// Retransmit 2xx response.
	// Send the response directly to the sender thread
	// as the INVITE transaction completed already.
	// (see RFC 3261 17.2.1)
	if (!resp_invite) return; // there is no response to send
	resp_invite->get_destination(ip_port);
	if (ip_port.ipaddr == 0) {
		// This should not happen. The response has been
		// sent before so it should be possible to sent
		// it again. Ignore the timeout. When the ACK
		// guard timer expires, the dialog will be
		// cleaned up.
		return;
	}
	evq_sender->push_network(resp_invite, ip_port);
}

t_dialog_state t_dialog::get_state(void) const {
	return state;
}

void t_dialog::timeout(t_line_timer timer) {
	switch(state) {
	case DS_W4INVITE_RESP:
	case DS_W4INVITE_RESP2:
		state_w4invite_resp(timer);
		break;
	case DS_EARLY:
		state_early(timer);
		break;
	case DS_W4ACK:
		state_w4ack(timer);
		break;
	case DS_W4ACK_RE_INVITE:
		state_w4ack_re_invite(timer);
		break;
	case DS_W4RE_INVITE_RESP2:
		state_w4re_invite_resp(timer);
		break;
	case DS_W4ANSWER:
		state_w4answer(timer);
		break;
	case DS_CONFIRMED:
		state_confirmed(timer);
		break;
	default:
		// Timeout not expected in other states. Ignore.
		break;
	}
}

void t_dialog::timeout_sub(t_subscribe_timer timer, const string &event_type,
		const string &event_id)
{
	if (sub_refer &&
	    sub_refer->get_event_type() == event_type &&
	    sub_refer->get_event_id() == event_id)
	{
		sub_refer->timeout(timer);
	} else {
		// Timeout does not match with the current subscription.
		// Ignore.
		return;
	}

	if (sub_refer->get_state() == SS_TERMINATED && state == DS_CONFIRMED_SUB) {
		MEMMAN_DELETE(sub_refer);
		delete sub_refer;
		sub_refer = NULL;
		state = DS_TERMINATED;
	}
}

t_phone *t_dialog::get_phone(void) const {
	return line->get_phone();
}

t_line *t_dialog::get_line(void) const {
	return line;
}

t_session *t_dialog::get_session(void) const {
	return session;
}

t_audio_session *t_dialog::get_audio_session(void) const {
	if (!session) return NULL;

	return session->get_audio_session();
}

bool t_dialog::has_active_session(void) const {
	if (session) return session->is_rtp_active();
	
	return false;
}

// RFC 3515
// Send a NOTIFY with reference progress to the referror
void t_dialog::notify_refer_progress(t_response *r) {
	if (!sub_refer) return;

	if (r->is_final()) {
		sub_refer->send_notify(r, SUBSTATE_TERMINATED, EV_REASON_NORESOURCE);
	} else {
		sub_refer->send_notify(r, SUBSTATE_ACTIVE);
	}
}

bool t_dialog::will_release(void) const {
	return state == DS_W4BYE_RESP || request_cancelled || 
		end_after_2xx_invite || end_after_ack;
}
