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

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

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

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

#include <assert.h>
#include "line.h"
#include "log.h"
#include "phone.h"
#include "phone_user.h"
#include "session.h"
#include "util.h"
#include "userintf.h"
#include "audits/memman.h"

extern string user_host;
extern t_phone *phone;

///////////
// PRIVATE
///////////

void t_session::set_recvd_codecs(t_sdp *sdp) {
	recvd_codecs.clear();
	send_ac2payload.clear();
	send_payload2ac.clear();
	list<unsigned short> payloads = sdp->get_codecs(SDP_AUDIO);
	for (list<unsigned short>::iterator i = payloads.begin(); 
	     i != payloads.end(); i++)
	{
		t_audio_codec ac = sdp->get_codec(SDP_AUDIO, *i);
		if (ac > CODEC_UNSUPPORTED) {
			recvd_codecs.push_back(ac);
			send_ac2payload[ac] = *i;
			send_payload2ac[*i] = ac;
		}
	}
}

bool t_session::is_3way(void) const {
	t_line *l = get_line();
	t_phone *p = l->get_phone();

	return p->part_of_3way(l->get_line_number());
}

t_session *t_session::get_peer_3way(void) const {
	t_line *l = get_line();
	t_phone *p = l->get_phone();

	t_line *peer_line = p->get_3way_peer_line(l->get_line_number());

	return peer_line->get_session();
}

///////////
// PUBLIC
///////////

t_session::t_session(t_dialog *_dialog, string _receive_host,
		  unsigned short _receive_port)
{
	dialog = _dialog;
	
	user_config = dialog->get_line()->get_user();
	assert(user_config);

	receive_host = _receive_host;
	retrieve_host = _receive_host;
	receive_port = _receive_port;
	src_sdp_version = int2str(rand());
	src_sdp_id = int2str(rand());
	use_codec = CODEC_NULL;
	
	switch (user_config->get_dtmf_transport()) {
	case DTMF_RFC2833:
	case DTMF_AUTO:
		recv_dtmf_pt = user_config->get_dtmf_payload_type();
		break;
	default:
		recv_dtmf_pt = 0;
	}
	
	send_dtmf_pt = 0;

	offer_codecs = user_config->get_codecs();
	ptime = user_config->get_ptime();
	ilbc_mode = user_config->get_ilbc_mode();

	recvd_offer = false;
	recvd_answer = false;
	sent_offer = false;
	direction = SDP_SENDRECV;

	audio_rtp_session = NULL;
	is_on_hold = false;
	is_killed = false;
	
	// Initialize audio codec to payload mappings
	recv_ac2payload[CODEC_G711_ULAW] = SDP_FORMAT_G711_ULAW;
	recv_ac2payload[CODEC_G711_ALAW] = SDP_FORMAT_G711_ALAW;
	recv_ac2payload[CODEC_GSM] = SDP_FORMAT_GSM;
	recv_ac2payload[CODEC_SPEEX_NB] = user_config->get_speex_nb_payload_type();
	recv_ac2payload[CODEC_SPEEX_WB] = user_config->get_speex_wb_payload_type();
	recv_ac2payload[CODEC_SPEEX_UWB] = user_config->get_speex_uwb_payload_type();
	recv_ac2payload[CODEC_ILBC] = user_config->get_ilbc_payload_type();
	recv_ac2payload[CODEC_G726_16] = user_config->get_g726_16_payload_type();
	recv_ac2payload[CODEC_G726_24] = user_config->get_g726_24_payload_type();
	recv_ac2payload[CODEC_G726_32] = user_config->get_g726_32_payload_type();
	recv_ac2payload[CODEC_G726_40] = user_config->get_g726_40_payload_type();
	recv_ac2payload[CODEC_TELEPHONE_EVENT] = user_config->get_dtmf_payload_type();
	send_ac2payload.clear();
	
	// Initialize pauload to audio codec mappings
	recv_payload2ac[SDP_FORMAT_G711_ULAW] = CODEC_G711_ULAW;
	recv_payload2ac[SDP_FORMAT_G711_ALAW] = CODEC_G711_ALAW;
	recv_payload2ac[SDP_FORMAT_GSM] = CODEC_GSM;
	recv_payload2ac[user_config->get_speex_nb_payload_type()] = CODEC_SPEEX_NB;
	recv_payload2ac[user_config->get_speex_wb_payload_type()] = CODEC_SPEEX_WB;
	recv_payload2ac[user_config->get_speex_uwb_payload_type()] = CODEC_SPEEX_UWB;
	recv_payload2ac[user_config->get_ilbc_payload_type()] = CODEC_ILBC;
	recv_payload2ac[user_config->get_g726_16_payload_type()] = CODEC_G726_16;
	recv_payload2ac[user_config->get_g726_24_payload_type()] = CODEC_G726_24;
	recv_payload2ac[user_config->get_g726_32_payload_type()] = CODEC_G726_32;
	recv_payload2ac[user_config->get_g726_40_payload_type()] = CODEC_G726_40;
	recv_payload2ac[user_config->get_dtmf_payload_type()] = CODEC_TELEPHONE_EVENT;
	send_payload2ac.clear();
}

t_session::~t_session() {
	stop_rtp();
}

t_session *t_session::create_new_version(void) const {
	t_session *s = new t_session(*this);
	MEMMAN_NEW(s);
	s->src_sdp_version = int2str(atoi(src_sdp_version.c_str()) + 1);
	s->recvd_codecs.clear();
	s->recvd_offer = false;
	s->recvd_answer = false;
	s->sent_offer = false;

	// Do not copy the RTP session
	s->set_audio_session(NULL);
	
	// Clear the codec to payload mappings as a new response must
	// be received from the far end
	s->send_ac2payload.clear();
	s->send_payload2ac.clear();

	return s;
}

t_session *t_session::create_call_hold(void) const {
	t_session *s = create_new_version();

	if (user_config->get_hold_variant() == HOLD_RFC2543) {
		s->receive_host = "0.0.0.0";
	} else if (user_config->get_hold_variant() == HOLD_RFC3264) {
		// RFC 3264 8.4
		if (direction == SDP_SENDRECV) {
			s->direction = SDP_SENDONLY;
		}
		else if (direction == SDP_RECVONLY) {
			s->direction = SDP_INACTIVE;
		}
	} else {
		assert(false);
	}
	
	// Prevent RTP from being started for this session as long
	// as the call is put on hold. Without this, the RTP sessions
	// will get started when a re-INVITE is received from the far-end
	// while the call is still locally on-hold.
	s->hold();

	return s;
}

t_session *t_session::create_call_retrieve(void) const {
	t_session *s = create_new_version();

	if (user_config->get_hold_variant() == HOLD_RFC2543) {
		s->receive_host = retrieve_host;
	} else if (user_config->get_hold_variant() == HOLD_RFC3264) {
		// RFC 3264 8.4
		if (direction == SDP_SENDONLY) {
			s->direction = SDP_SENDRECV;
		}
		else if (direction == SDP_INACTIVE) {
			s->direction = SDP_RECVONLY;
		}
	} else {
		assert(false);
	}

	return s;
}

t_session *t_session::create_clean_copy(void) const {
	t_session *s = new t_session(*this);
	MEMMAN_NEW(s);
	s->src_sdp_version = int2str(atoi(src_sdp_version.c_str()) + 1);
	s->dst_sdp_version = "";
	s->dst_sdp_id = "";
	s->dst_rtp_host = "";
	s->dst_rtp_port = 0;
	s->recvd_codecs.clear();
	s->recvd_offer = false;
	s->recvd_answer = false;
	s->sent_offer = false;
	s->direction = SDP_SENDRECV;

	// Do not copy the RTP session
	s->set_audio_session(NULL);
	
	// Clear the codec to payload mappings as a new response must
	// be received from the far end
	s->send_ac2payload.clear();
	s->send_payload2ac.clear();

	return s;
}

bool t_session::process_sdp_offer(t_sdp *sdp, int &warn_code,
		string &warn_text)
{
	if (!sdp->is_supported(warn_code, warn_text)) return false;

	dst_sdp_version = sdp->origin.session_version;
	dst_sdp_id = sdp->origin.session_id;
	recvd_sdp_offer = *sdp;
	
	// RFC 3264 5
	// SDP may contain 0 m= lines
	if (sdp->media.empty()) return true;
	
	dst_rtp_host = sdp->get_rtp_host(SDP_AUDIO);
	dst_rtp_port = sdp->get_rtp_port(SDP_AUDIO);
	set_recvd_codecs(sdp);
	dst_zrtp_support = sdp->get_zrtp_support(SDP_AUDIO);

	// The direction in the SDP is from the point of view of the
	// far end. Swap the direction to store it as the point of view
	// from the near end.
	switch(sdp->get_direction(SDP_AUDIO)) {
	case SDP_INACTIVE:
		direction = SDP_INACTIVE;
		break;
	case SDP_SENDONLY:
		if (is_on_hold && user_config->get_hold_variant() == HOLD_RFC3264) {
			// The phone is put on-hold. We don't want to
			// receive media.
			direction = SDP_INACTIVE;
		} else {
			direction = SDP_RECVONLY;
		}
		break;
	case SDP_RECVONLY:
		direction = SDP_SENDONLY;
		break;
	case SDP_SENDRECV:
		if (is_on_hold && user_config->get_hold_variant() == HOLD_RFC3264) {
			// The phone is put on-hold. We don't want to
			// receive media.
			direction = SDP_SENDONLY;
		} else {
			direction = SDP_SENDRECV;
		}
		break;
	default:
		assert(false);
	}

	// Check if the list of received codecs has at least 1 codec
	// in common with the list of codecs we can offer. If there
	// is no common codec, then no call can be established.
	list<t_audio_codec>::iterator supported_codec_it = offer_codecs.end();
	for (list<t_audio_codec>::const_iterator i = recvd_codecs.begin();
	     i != recvd_codecs.end(); i++)
	{
		list<t_audio_codec>::iterator tmp_it;
		if ((supported_codec_it == offer_codecs.end() ||
		     !user_config->get_in_obey_far_end_codec_pref()) &&
		    (tmp_it = std::find(offer_codecs.begin(), supported_codec_it, *i)) !=
					supported_codec_it)
		{
			// Codec supported
			supported_codec_it = tmp_it;
			use_codec = *i; // this codec goes into answer
			
			// Use the payload to codec bindings as signalled in the
			// offer by the far end.
			recv_payload2ac[send_ac2payload[use_codec]] = use_codec;
			recv_ac2payload[use_codec] = send_ac2payload[use_codec];
		} else if (*i == CODEC_TELEPHONE_EVENT) {
			// telephone-event payload is supported
			send_dtmf_pt = send_ac2payload[*i];
			
			// When we support RFC 2833 events, then take the payload
			// type from the far end.
			if (recv_dtmf_pt > 0) {
				recv_dtmf_pt = send_dtmf_pt; // this goes into answer as well
			}
		}
	}

	if (supported_codec_it == offer_codecs.end()) {
		warn_code = W_305_INCOMPATIBLE_MEDIA_FORMAT;
		warn_text = "None of the audio codecs is supported";
		return false;
	}

	// Overwrite ptime value with ptime from SDP
	unsigned short p = sdp->get_ptime(SDP_AUDIO);
	if (p > 0) ptime = p;
	
	// RFC 3952 5
	// Select the iLBC mode that needs the lowest bandwidth
	if (use_codec == CODEC_ILBC) {
		int recvd_mode = sdp->get_fmtp_int_param(SDP_AUDIO, 
				send_ac2payload[use_codec], "mode");
		if (recvd_mode == -1) recvd_mode = 30;
		if (VALID_ILBC_MODE(recvd_mode) && recvd_mode > ilbc_mode) {
			ilbc_mode = static_cast<unsigned short>(recvd_mode);
		}
	}

	return true;
}

bool t_session::process_sdp_answer(t_sdp *sdp, int &warn_code,
		string &warn_text)
{
	if (!sdp->is_supported(warn_code, warn_text)) return false;
	
	// As our offer always contains an audio m= line, the answer
	// should contain one as well. If there are media lines, then
	// the sdp->is_supported already verified there is audio.
	if (sdp->media.empty()) {
		warn_code = W_304_MEDIA_TYPE_NOT_AVAILABLE;
		warn_text = "Valid media stream for audio is missing";
		return false;
	}

	dst_sdp_version = sdp->origin.session_version;
	dst_sdp_id = sdp->origin.session_id;
	dst_rtp_host = sdp->get_rtp_host(SDP_AUDIO);
	dst_rtp_port = sdp->get_rtp_port(SDP_AUDIO);
	dst_zrtp_support = sdp->get_zrtp_support(SDP_AUDIO);
	set_recvd_codecs(sdp);

	// Find the first codec in the received codecs list that
	// is supported.
	// Per the offer/answer model all received codecs should be
	// supported! It seems that some applications put more codecs
	// in the answer though.
	list<t_audio_codec>::iterator codec_found_it = offer_codecs.end();

	for (list<t_audio_codec>::const_iterator i = recvd_codecs.begin();
	     i != recvd_codecs.end(); i++)
	{
		list<t_audio_codec>::iterator tmp_it;
		if ((codec_found_it == offer_codecs.end() ||
		     !user_config->get_out_obey_far_end_codec_pref()) &&
		    (tmp_it = std::find(offer_codecs.begin(), codec_found_it, *i)) !=
				codec_found_it)
		{
			codec_found_it = tmp_it;
			use_codec = *i;
		} else if (*i == CODEC_TELEPHONE_EVENT) {
			// telephone-event payload is supported
			send_dtmf_pt = send_ac2payload[*i];
		}
	}

	if (codec_found_it == offer_codecs.end()) {
		// None of the answered codecs is supported
		warn_code = W_305_INCOMPATIBLE_MEDIA_FORMAT;
		warn_text = "None of the codecs is supported";
		return false;
	}

	// Overwrite ptime value with ptime from SDP
	unsigned short p = sdp->get_ptime(SDP_AUDIO);
	if (p > 0) ptime = p;
	
	// RFC 3952 5
	// Select the iLBC mode that needs the lowest bandwidth
	if (use_codec == CODEC_ILBC) {
		int recvd_mode = sdp->get_fmtp_int_param(SDP_AUDIO, 
				send_ac2payload[use_codec], "mode");
		if (recvd_mode == -1) recvd_mode = 30;
		if (VALID_ILBC_MODE(recvd_mode) && recvd_mode > ilbc_mode) {
			ilbc_mode = static_cast<unsigned short>(recvd_mode);
		}
	}

	return true;
}

void t_session::create_sdp_offer(t_sip_message *m, const string &user) {
	// Delete old body if present
	if (m->body) {
		MEMMAN_DELETE(m->body);
		delete m->body;
	}

	m->body = new t_sdp(user, src_sdp_id, src_sdp_version, USER_HOST(user_config),
			receive_host, receive_port, offer_codecs, recv_dtmf_pt,
			recv_ac2payload);
	MEMMAN_NEW(m->body);


	// Set ptime for G711/G726 codecs
	list<t_audio_codec>::iterator it_g7xx;
	it_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G711_ALAW);
	if (it_g7xx == offer_codecs.end()) {
		it_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G711_ULAW);
	}
	if (it_g7xx == offer_codecs.end()) {
		it_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G726_16);
	}
	if (it_g7xx == offer_codecs.end()) {
		it_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G726_24);
	}
	if (it_g7xx == offer_codecs.end()) {
		it_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G726_32);
	}
	if (it_g7xx == offer_codecs.end()) {
		it_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G726_40);
	}
	if (it_g7xx != offer_codecs.end()) {
		((t_sdp *)m->body)->set_ptime(SDP_AUDIO, ptime);
	}
	
	// Set mode for iLBC codecs
	list<t_audio_codec>::iterator it_ilbc;
	it_ilbc = find(offer_codecs.begin(), offer_codecs.end(), CODEC_ILBC);
	if (it_ilbc != offer_codecs.end() && ilbc_mode != 30) {
		((t_sdp *)m->body)->set_fmtp_int_param(SDP_AUDIO, recv_ac2payload[CODEC_ILBC],
				"mode", ilbc_mode);
	}

	// Set direction
	if (direction != SDP_SENDRECV) {
		((t_sdp *)m->body)->set_direction(SDP_AUDIO, direction);
	}
	
	// Set zrtp support
	if (user_config->get_zrtp_enabled() && user_config->get_zrtp_sdp()) {
		((t_sdp *)m->body)->set_zrtp_support(SDP_AUDIO);
	}

	m->hdr_content_type.set_media(t_media("application", "sdp"));

	sent_offer = true;
}

void t_session::create_sdp_answer(t_sip_message *m, const string &user) const {
	// Delete old body if present
	if (m->body) {
		MEMMAN_DELETE(m->body);
		delete m->body;
	}

	list<t_audio_codec> answer_codecs;
	answer_codecs.push_back(use_codec);

	// RFC 3264 6
	// The answer must contain an m-line for each m-line in the offer in
	// the same order. Media can be rejected by setting the port to 0.
	// Only the first audio stream is accepted, all other media streams
	// will be rejected.
	m->body = new t_sdp(user, src_sdp_id, src_sdp_version, USER_HOST(user_config),
				receive_host);
	MEMMAN_NEW(m->body);
	bool audio_answered = false;
	for (list<t_sdp_media>::const_iterator i = recvd_sdp_offer.media.begin();
	     i != recvd_sdp_offer.media.end(); i++)
	{
		if (!audio_answered && i->get_media_type() == SDP_AUDIO &&
		    i->port != 0)
		{
			// Accept the first audio stream
			((t_sdp *)m->body)->add_media(t_sdp_media(
				SDP_AUDIO, receive_port, answer_codecs, recv_dtmf_pt,
				send_ac2payload));
			audio_answered = true;
		}
		else
		{
			// Reject media stream by setting port to zero
			t_sdp_media reject_media(*i);
			reject_media.port = 0;
			((t_sdp *)m->body)->add_media(reject_media);
		}
	}
	
	m->hdr_content_type.set_media(t_media("application", "sdp"));
	
	// If there were no media lines in the offer, we sent no media
	// lines in the answer
	if (recvd_sdp_offer.media.empty()) return;

	// Set audio attributes
	
	// Set ptime for G711 codecs
	if (use_codec == CODEC_G711_ALAW ||
	    use_codec == CODEC_G711_ULAW)
	{
		((t_sdp *)m->body)->set_ptime(SDP_AUDIO, ptime);
	}

	// Set mode for iLBC codecs
	if (use_codec == CODEC_ILBC && ilbc_mode != 30) {
		unsigned short ilbc_payload = const_cast<t_session *>(this)->
				recv_ac2payload[CODEC_ILBC];
		((t_sdp *)m->body)->set_fmtp_int_param(SDP_AUDIO, ilbc_payload,
				"mode", ilbc_mode);
	}
	
	// Set direction
	if (direction != SDP_SENDRECV) {
		((t_sdp *)m->body)->set_direction(SDP_AUDIO, direction);
	}
	
	// Set zrtp support
	if (user_config->get_zrtp_enabled() && user_config->get_zrtp_sdp()) {
		((t_sdp *)m->body)->set_zrtp_support(SDP_AUDIO);
	}
}

void t_session::start_rtp(void) {
	t_audio_codec codec;
	
	// If a session is killed, it may not be started again.
	if (is_killed) {
		log_file->write_report("Cannot start. The session is killed already.",
			"t_session::start_rtp", LOG_NORMAL, LOG_DEBUG);
		return;
	}
	
	// If a session is on-hold then do not start RTP.
	if (is_on_hold) {
		log_file->write_report("Cannot start. The session is on hold.",
			"t_session::start_rtp", LOG_NORMAL, LOG_DEBUG);
		return;
	}

	if (receive_host.empty()) {
		log_file->write_report("Cannot start. receive_host is empty.",
			"t_session::start_rtp", LOG_NORMAL, LOG_DEBUG);
		return;
	}
	
	if (dst_rtp_host.empty()) {
		log_file->write_report("Cannot start. dst_rtp_host is empty.",
			"t_session::start_rtp", LOG_NORMAL, LOG_DEBUG);
		return;
	}

	// Local and remote hold
	if (((receive_host == "0.0.0.0" || receive_port == 0) &&
	     (dst_rtp_host == "0.0.0.0" || dst_rtp_port == 0)) ||
 	    direction == SDP_INACTIVE)
	{
		log_file->write_report("Cannot start. Local and remote on hold.",
			"t_session::start_rtp", LOG_NORMAL, LOG_DEBUG);
		return;
	}

	// Inform user about the codecs
	get_line()->ci_set_send_codec(use_codec);
	get_line()->ci_set_recv_codec(use_codec);
	ui->cb_send_codec_changed(get_line()->get_line_number(), use_codec);
	ui->cb_recv_codec_changed(get_line()->get_line_number(), use_codec);
	
	// Determine ptime
	unsigned short audio_ptime;
	if (use_codec == CODEC_ILBC) {
		audio_ptime = ilbc_mode;
	} else {
		audio_ptime = ptime;
	}
	
	// Determine if audio must be encrypted
	bool encrypt_audio = get_line()->get_try_to_encrypt();
	if (user_config->get_zrtp_send_if_supported()) {
		encrypt_audio = encrypt_audio && dst_zrtp_support;
	}

	// Start the RTP streams
	if (dst_rtp_host == "0.0.0.0" || dst_rtp_port == 0 ||
	    direction == SDP_RECVONLY)
	{
		// Local hold -> do not send RTP
		log_file->write_report("Local hold. Do not send RTP.",
			"t_session::start_rtp", LOG_NORMAL, LOG_DEBUG);
		audio_rtp_session = new t_audio_session(this,
				LOCAL_IP, get_line()->get_rtp_port(), "", 0, use_codec, 
				audio_ptime, recv_payload2ac, send_ac2payload,
				encrypt_audio);
		MEMMAN_NEW(audio_rtp_session);
	}
	else if (receive_host == "0.0.0.0" || receive_port == 0 ||
	         direction == SDP_SENDONLY)
	{
		// Remote hold
		// For music on-hold music should be played here.
		// Without music on-hold do not send out RTP
		/*
		audio_rtp_session = new t_audio_session(this,
				"", 0, dst_rtp_host, dst_rtp_port, codec, ptime);
		*/
		log_file->write_report("Do not start. Remote hold.",
			"t_session::start_rtp", LOG_NORMAL, LOG_DEBUG);
		return;
	} else {
		// Bi-directional audio
		audio_rtp_session = new t_audio_session(this,
				LOCAL_IP, get_line()->get_rtp_port(),
				dst_rtp_host, dst_rtp_port, use_codec, audio_ptime,
				recv_payload2ac, send_ac2payload,
				encrypt_audio);
		MEMMAN_NEW(audio_rtp_session);
	}

	// Check if the created audio session is valid.
	if (!audio_rtp_session->is_valid()) {
		log_file->write_report("Audio session is invalid.",
			"t_session::start_rtp", LOG_NORMAL, LOG_CRITICAL);
		MEMMAN_DELETE(audio_rtp_session);
		delete audio_rtp_session;
		audio_rtp_session = NULL;
		return;
	}

	// Set dynamic payload type for DTMF events
	if (recv_dtmf_pt > 0) {
		unsigned short alt_dtmf_pt;
		if (recv_payload2ac.find(send_dtmf_pt) == recv_payload2ac.end()) {
			// Allow the payload type as signalled by the far end
			// as an alternative to the payload as signalled by Twinkle.
			alt_dtmf_pt = send_dtmf_pt;
		} else {
			// The payload type as signalled by the far end for DTMF
			// is already in use by Twinkle for another codec, so it
			// cannot be used as an alternative.
			alt_dtmf_pt = recv_dtmf_pt;
		}
		audio_rtp_session->set_pt_in_dtmf(recv_dtmf_pt, alt_dtmf_pt);
	}

	if (send_dtmf_pt > 0) {
		audio_rtp_session->set_pt_out_dtmf(send_dtmf_pt);
		
		switch (user_config->get_dtmf_transport()) {
		case DTMF_AUTO:
		case DTMF_RFC2833:
			get_line()->ci_set_dtmf_supported(true, false);
			break;
		case DTMF_INBAND:
			get_line()->ci_set_dtmf_supported(true, true);
			break;
		case DTMF_INFO:
			get_line()->ci_set_dtmf_supported(true, false, true);
			break;
		default:
			assert(false);
		}
		
		ui->cb_dtmf_supported(get_line()->get_line_number());
	} else {
		switch (user_config->get_dtmf_transport()) {
		case DTMF_AUTO:
		case DTMF_INBAND:
			get_line()->ci_set_dtmf_supported(true, true);
			ui->cb_dtmf_supported(get_line()->get_line_number());
			break;
		case DTMF_RFC2833:
			get_line()->ci_set_dtmf_supported(false);
			ui->cb_dtmf_not_supported(get_line()->get_line_number());
			break;
		case DTMF_INFO:
			get_line()->ci_set_dtmf_supported(true, false, true);
			ui->cb_dtmf_supported(get_line()->get_line_number());
			break;
		default:
			assert(false);
		}
	}

	audio_rtp_session->run();
}

void t_session::stop_rtp(void) {
	if (audio_rtp_session) {
		MEMMAN_DELETE(audio_rtp_session);
		delete audio_rtp_session;
		audio_rtp_session = NULL;
	
		get_line()->ci_set_dtmf_supported(false);
		ui->cb_line_state_changed();
	}
}

void t_session::kill_rtp(void) {
	stop_rtp();
	is_killed = true;
}

t_audio_session *t_session::get_audio_session(void) const {
	return audio_rtp_session;
}

void t_session::set_audio_session(t_audio_session *as) {
	audio_rtp_session = as;
}

bool t_session::equal_audio(const t_session &s) const {
	// According to RFC 3264 6, the SDP version in the o= line
	// must be updated when the SDP is changed.
	// We check for more changes to interoperate with SIP
	// devices that do not adhere fully to RFC 3264
	return (receive_host == s.receive_host &&
		receive_port == s.receive_port &&
		dst_rtp_host == s.dst_rtp_host &&
		dst_rtp_port == s.dst_rtp_port &&
		direction == s.direction &&
		src_sdp_version == s.src_sdp_version &&
		dst_sdp_version == s.dst_sdp_version &&
		src_sdp_id == s.src_sdp_id &&
		dst_sdp_id == s.dst_sdp_id);
}

void t_session::send_dtmf(char digit, bool inband) {
	if (audio_rtp_session) audio_rtp_session->send_dtmf(digit, inband);
}

t_line *t_session::get_line(void) const {
	return dialog->get_line();
}

void t_session::set_owner(t_dialog *d) {
	dialog = d;
}

void t_session::hold(void) {
	is_on_hold = true;
}

void t_session::unhold(void) {
	is_on_hold = false;
}

bool t_session::is_rtp_active(void) const {
	return (audio_rtp_session != NULL);
}


syntax highlighted by Code2HTML, v. 0.9.1