/*
Copyright (C) 2005-2007 Michel de Boer <michel@twinklephone.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <assert.h>
#include <iostream>
#include <signal.h>
#include "exceptions.h"
#include "line.h"
#include "log.h"
#include "sdp/sdp.h"
#include "util.h"
#include "user.h"
#include "userintf.h"
#include "audits/memman.h"
extern t_event_queue *evq_timekeeper;
///////////////
// t_call_info
///////////////
t_call_info::t_call_info() {
clear();
}
void t_call_info::clear(void) {
from_uri.set_url("");
from_display.clear();
from_display_override.clear();
from_organization.clear();
to_uri.set_url("");
to_display.clear();
to_organization.clear();
subject.clear();
dtmf_supported = false;
hdr_referred_by = t_hdr_referred_by();
last_provisional_reason.clear();
send_codec = CODEC_NULL;
recv_codec = CODEC_NULL;
refer_supported = false;
}
string t_call_info::get_from_display_presentation(void) const {
if (from_display_override.empty()) {
return from_display;
} else {
return from_display_override;
}
}
///////////
// t_line
///////////
///////////
// Private
///////////
t_dialog *t_line::match_response(t_response *r,
const list<t_dialog *> &l) const
{
list<t_dialog *>::const_iterator i;
for (i = l.begin(); i != l.end(); i++) {
if ((*i)->match_response(r, 0)) return *i;
}
return NULL;
}
t_dialog *t_line::match_response(StunMessage *r, t_tuid tuid,
const list<t_dialog *> &l) const
{
list<t_dialog *>::const_iterator i;
for (i = l.begin(); i != l.end(); i++) {
if ((*i)->match_response(r, tuid)) return *i;
}
return NULL;
}
t_dialog *t_line::match_call_id_tags(const string &call_id,
const string &to_tag, const string &from_tag,
const list<t_dialog *> &l) const
{
list<t_dialog *>::const_iterator i;
for (i = l.begin(); i != l.end(); i++) {
if ((*i)->match(call_id, to_tag, from_tag)) return *i;
}
return NULL;
}
t_dialog *t_line::get_dialog(t_object_id did) const {
list<t_dialog *>::const_iterator i;
if (did == 0) return NULL;
if (open_dialog && open_dialog->get_object_id() == did) {
return open_dialog;
}
if (active_dialog && active_dialog->get_object_id() == did) {
return active_dialog;
}
for (i = pending_dialogs.begin(); i != pending_dialogs.end(); i++) {
if ((*i)->get_object_id() == did) return *i;
}
for (i = dying_dialogs.begin(); i != dying_dialogs.end(); i++) {
if ((*i)->get_object_id() == did) return *i;
}
return NULL;
}
void t_line::cleanup(void) {
list<t_dialog *>::iterator i;
if (open_dialog && open_dialog->get_state() == DS_TERMINATED) {
MEMMAN_DELETE(open_dialog);
delete open_dialog;
open_dialog = NULL;
}
if (active_dialog && active_dialog->get_state() == DS_TERMINATED) {
MEMMAN_DELETE(active_dialog);
delete active_dialog;
active_dialog = NULL;
stop_timer(LTMR_INVITE_COMP);
stop_timer(LTMR_NO_ANSWER);
// If the call has been ended within 64*T1 seconds
// after the reception of the first 2XX response, there
// might still be open and pending dialogs. To be nice these
// dialogs should be kept till the 64*T1 timer expires.
// This complicates the setup of new call however. For
// now the dialogs will be killed. If a slow UAS
// still responds, it has bad luck and will time out.
//
// TODO:
// A nice solution would be to move the pending and open
// dialog to the dying dialog and start a new time 64*T1
// timer to keep the dying dialogs alive. A sequence of
// a few short calls would add to the dying dialogs and
// keep some dialogs alive longer than necessary. This
// only has an impact on resources, not on signalling.
// Note that the open dialog must be appended after the
// pending dialogs, otherwise all received responses for
// a pending dialog will match the open dialog if that
// match is tried first by match_response()
for (i = pending_dialogs.begin(); i != pending_dialogs.end();
i++)
{
MEMMAN_DELETE(*i);
delete *i;
}
pending_dialogs.clear();
if (open_dialog) {
MEMMAN_DELETE(open_dialog);
delete open_dialog;
}
open_dialog = NULL;
}
if (active_dialog) {
if (active_dialog->get_state() == DS_CONFIRMED_SUB) {
// The calls have been released but a subscription is
// still active.
substate = LSSUB_RELEASING;
} else if (active_dialog->will_release()) {
substate = LSSUB_RELEASING;
}
}
for (i = pending_dialogs.begin(); i != pending_dialogs.end(); i++) {
if ((*i)->get_state() == DS_TERMINATED) {
MEMMAN_DELETE(*i);
delete *i;
*i = NULL;
}
}
pending_dialogs.remove(NULL);
for (i = dying_dialogs.begin(); i != dying_dialogs.end(); i++) {
if ((*i)->get_state() == DS_TERMINATED) {
MEMMAN_DELETE(*i);
delete *i;
*i = NULL;
}
}
dying_dialogs.remove(NULL);
if (!open_dialog && !active_dialog && pending_dialogs.size() == 0) {
state = LS_IDLE;
if (keep_seized) {
substate = LSSUB_SEIZED;
} else {
substate = LSSUB_IDLE;
}
is_on_hold = false;
is_muted = false;
hide_user = false;
cleanup_transfer_consult_state();
try_to_encrypt = false;
auto_answer = false;
call_info.clear();
call_history->add_call_record(call_hist_record);
call_hist_record.renew();
user_config = NULL;
user_defined_ringtone.clear();
ui->cb_line_state_changed();
}
}
void t_line::cleanup_open_pending(void) {
if (open_dialog) {
MEMMAN_DELETE(open_dialog);
delete open_dialog;
open_dialog = NULL;
}
list<t_dialog *>::iterator i;
for (i = pending_dialogs.begin(); i != pending_dialogs.end(); i++) {
MEMMAN_DELETE(*i);
delete *i;
}
pending_dialogs.clear();
if (!active_dialog) {
is_on_hold = false;
is_muted = false;
hide_user = false;
cleanup_transfer_consult_state();
try_to_encrypt = false;
auto_answer = false;
state = LS_IDLE;
if (keep_seized) {
substate = LSSUB_SEIZED;
} else {
substate = LSSUB_IDLE;
}
call_info.clear();
call_history->add_call_record(call_hist_record);
call_hist_record.renew();
user_config = NULL;
user_defined_ringtone.clear();
ui->cb_line_state_changed();
}
}
void t_line::cleanup_forced(void) {
list<t_dialog *>::iterator i;
if (open_dialog) {
MEMMAN_DELETE(open_dialog);
delete open_dialog;
open_dialog = NULL;
}
if (active_dialog) {
MEMMAN_DELETE(active_dialog);
delete active_dialog;
active_dialog = NULL;
}
for (i = pending_dialogs.begin(); i != pending_dialogs.end(); i++) {
MEMMAN_DELETE(*i);
delete *i;
*i = NULL;
}
pending_dialogs.remove(NULL);
for (i = dying_dialogs.begin(); i != dying_dialogs.end(); i++) {
MEMMAN_DELETE(*i);
delete *i;
*i = NULL;
}
dying_dialogs.remove(NULL);
// TODO: stop running timers?
state = LS_IDLE;
substate = LSSUB_IDLE;
keep_seized = false;
is_on_hold = false;
is_muted = false;
hide_user = false;
cleanup_transfer_consult_state();
auto_answer = false;
call_info.clear();
call_history->add_call_record(call_hist_record);
call_hist_record.renew();
user_config = NULL;
user_defined_ringtone.clear();
ui->cb_line_state_changed();
}
void t_line::cleanup_transfer_consult_state(void) {
if (is_transfer_consult) {
t_line *from_line = phone->get_line(consult_transfer_from_line);
from_line->set_to_be_transferred(false, 0);
is_transfer_consult = false;
}
if (to_be_transferred) {
t_line *to_line = phone->get_line(consult_transfer_to_line);
to_line->set_is_transfer_consult(false, 0);
to_be_transferred = false;
}
}
///////////
// Public
///////////
t_line::t_line(t_phone *_phone, unsigned short _line_number) :
t_id_object()
{
// NOTE: The rtp_port attribute can only be initialized when
// a user profile has been selected.
phone = _phone;
state = LS_IDLE;
substate = LSSUB_IDLE;
open_dialog = NULL;
active_dialog = NULL;
is_on_hold = false;
is_muted = false;
hide_user = false;
is_transfer_consult = false;
to_be_transferred = false;
try_to_encrypt = false;
auto_answer = false;
line_number = _line_number;
id_invite_comp = 0;
id_no_answer = 0;
user_config = NULL;
user_defined_ringtone.clear();
keep_seized = false;
}
t_line::~t_line() {
list<t_dialog *>::iterator i;
// Stop timers
if (id_invite_comp) stop_timer(LTMR_INVITE_COMP);
if (id_no_answer) stop_timer(LTMR_NO_ANSWER);
// Delete pointers
if (open_dialog) {
MEMMAN_DELETE(open_dialog);
delete open_dialog;
}
if (active_dialog) {
MEMMAN_DELETE(active_dialog);
delete active_dialog;
}
// Delete dialogs
for (i = pending_dialogs.begin(); i != pending_dialogs.end(); i++) {
MEMMAN_DELETE(*i);
delete *i;
}
for (i = dying_dialogs.begin(); i != dying_dialogs.end(); i++) {
MEMMAN_DELETE(*i);
delete *i;
}
}
t_line_state t_line::get_state(void) const {
return state;
}
t_line_substate t_line::get_substate(void) const {
return substate;
}
t_refer_state t_line::get_refer_state(void) const {
if (active_dialog) return active_dialog->refer_state;
return REFST_NULL;
}
void t_line::start_timer(t_line_timer timer, t_object_id did) {
t_tmr_line *t;
t_dialog *dialog = get_dialog(did);
unsigned long dur;
switch(timer) {
case LTMR_ACK_TIMEOUT:
assert(dialog);
// RFC 3261 13.3.1.4
if (dialog->dur_ack_timeout == 0) {
dialog->dur_ack_timeout = DURATION_T1;
} else {
dialog->dur_ack_timeout *= 2;
if (dialog->dur_ack_timeout > DURATION_T2 ) {
dialog->dur_ack_timeout = DURATION_T2;
}
}
t = new t_tmr_line(dialog->dur_ack_timeout , timer, get_object_id(),
did);
MEMMAN_NEW(t);
dialog->id_ack_timeout = t->get_object_id();
break;
case LTMR_ACK_GUARD:
assert(dialog);
// RFC 3261 13.3.1.4
t = new t_tmr_line(64 * DURATION_T1, timer, get_object_id(), did);
MEMMAN_NEW(t);
dialog->id_ack_guard = t->get_object_id();
break;
case LTMR_INVITE_COMP:
// RFC 3261 13.2.2.4
t = new t_tmr_line(64 * DURATION_T1, timer, get_object_id(), did);
MEMMAN_NEW(t);
id_invite_comp = t->get_object_id();
break;
case LTMR_NO_ANSWER:
t = new t_tmr_line(DUR_NO_ANSWER(user_config), timer, get_object_id(), did);
MEMMAN_NEW(t);
id_no_answer = t->get_object_id();
break;
case LTMR_RE_INVITE_GUARD:
assert(dialog);
t = new t_tmr_line(DUR_RE_INVITE_GUARD, timer, get_object_id(), did);
MEMMAN_NEW(t);
dialog->id_re_invite_guard = t->get_object_id();
break;
case LTMR_GLARE_RETRY:
assert(dialog);
if (dialog->is_call_id_owner()) {
dur = DUR_GLARE_RETRY_OWN;
} else {
dur = DUR_GLARE_RETRY_NOT_OWN;
}
t = new t_tmr_line(dur, timer, get_object_id(), did);
MEMMAN_NEW(t);
dialog->id_glare_retry = t->get_object_id();
break;
case LTMR_100REL_TIMEOUT:
assert(dialog);
// RFC 3262 3
if (dialog->dur_100rel_timeout == 0) {
dialog->dur_100rel_timeout = DUR_100REL_TIMEOUT;
} else {
dialog->dur_100rel_timeout *= 2;
}
t = new t_tmr_line(dialog->dur_100rel_timeout , timer, get_object_id(),
did);
MEMMAN_NEW(t);
dialog->id_100rel_timeout = t->get_object_id();
break;
case LTMR_100REL_GUARD:
assert(dialog);
// RFC 3262 3
t = new t_tmr_line(DUR_100REL_GUARD, timer, get_object_id(), did);
MEMMAN_NEW(t);
dialog->id_100rel_guard = t->get_object_id();
break;
case LTMR_CANCEL_GUARD:
assert(dialog);
t = new t_tmr_line(DUR_CANCEL_GUARD, timer, get_object_id(), did);
MEMMAN_NEW(t);
dialog->id_cancel_guard = t->get_object_id();
break;
default:
assert(false);
}
evq_timekeeper->push_start_timer(t);
MEMMAN_DELETE(t);
delete t;
}
void t_line::stop_timer(t_line_timer timer, t_object_id did) {
t_object_id *id;
t_dialog *dialog = get_dialog(did);
switch(timer) {
case LTMR_ACK_TIMEOUT:
assert(dialog);
dialog->dur_ack_timeout = 0;
id = &dialog->id_ack_timeout;
break;
case LTMR_ACK_GUARD:
assert(dialog);
id = &dialog->id_ack_guard;
break;
case LTMR_INVITE_COMP:
id = &id_invite_comp;
break;
case LTMR_NO_ANSWER:
id = &id_no_answer;
break;
case LTMR_RE_INVITE_GUARD:
assert(dialog);
id = &dialog->id_re_invite_guard;
break;
case LTMR_GLARE_RETRY:
assert(dialog);
id = &dialog->id_glare_retry;
break;
case LTMR_100REL_TIMEOUT:
assert(dialog);
dialog->dur_100rel_timeout = 0;
id = &dialog->id_100rel_timeout;
break;
case LTMR_100REL_GUARD:
assert(dialog);
id = &dialog->id_100rel_guard;
break;
case LTMR_CANCEL_GUARD:
assert(dialog);
id = &dialog->id_cancel_guard;
// KLUDGE
if (*id == 0) {
// Cancel is always sent on the open dialog.
// The timer is probably stopped from a pending dialog,
// therefore the timer is stopped on the wrong dialog.
// Check if the open dialog has a CANCEL guard timer.
if (open_dialog) id = &open_dialog->id_cancel_guard;
}
break;
default:
assert(false);
}
if (*id != 0) evq_timekeeper->push_stop_timer(*id);
*id = 0;
}
void t_line::invite(t_user *user, const t_url &to_uri, const string &to_display,
const string &subject, bool anonymous)
{
invite(user, to_uri, to_display, subject, t_hdr_referred_by(),
t_hdr_replaces(), t_hdr_require(), anonymous);
}
void t_line::invite(t_user *user, 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, bool anonymous)
{
assert(user);
// Ignore if line is not idle
if (state != LS_IDLE) {
return;
}
assert(!open_dialog);
// Validate speaker and mic
string error_msg;
if (!sys_config->exec_audio_validation(false, true, true, error_msg)) {
ui->cb_show_msg(error_msg, MSG_CRITICAL);
return;
}
user_config = user;
call_info.from_uri = create_user_uri(); // NOTE: hide_user is not set yet
call_info.from_display = user_config->get_display(false);
call_info.from_organization = user_config->get_organization();
call_info.to_uri = to_uri;
call_info.to_display = to_display;
call_info.to_organization.clear();
call_info.subject = subject;
call_info.hdr_referred_by = hdr_referred_by;
try_to_encrypt = user_config->get_zrtp_enabled();
state = LS_BUSY;
substate = LSSUB_OUTGOING_PROGRESS;
hide_user = anonymous;
ui->cb_line_state_changed();
open_dialog = new t_dialog(this);
MEMMAN_NEW(open_dialog);
open_dialog->send_invite(to_uri, to_display, subject, hdr_referred_by,
hdr_replaces, hdr_require, anonymous);
cleanup();
}
void t_line::answer(void) {
// Ignore if line is idle
if (state == LS_IDLE) return;
assert(active_dialog);
// Validate speaker and mic
string error_msg;
if (!sys_config->exec_audio_validation(false, true, true, error_msg)) {
ui->cb_show_msg(error_msg, MSG_CRITICAL);
return;
}
stop_timer(LTMR_NO_ANSWER);
try {
substate = LSSUB_ANSWERING;
ui->cb_line_state_changed();
active_dialog->answer();
}
catch (t_exception x) {
// TODO: there is no call to answer
}
cleanup();
}
void t_line::reject(void) {
// Ignore if line is idle
if (state == LS_IDLE) return;
assert(active_dialog);
stop_timer(LTMR_NO_ANSWER);
try {
active_dialog->reject(R_603_DECLINE);
}
catch (t_exception x) {
// TODO: there is no call to reject
}
cleanup();
}
void t_line::redirect(const list<t_display_url> &destinations, int code, string reason)
{
// Ignore if line is idle
if (state == LS_IDLE) return;
assert(active_dialog);
stop_timer(LTMR_NO_ANSWER);
try {
active_dialog->redirect(destinations, code, reason);
}
catch (t_exception x) {
// TODO: there is no call to redirect
}
cleanup();
}
void t_line::end_call(void) {
// Ignore if phone is idle
if (state == LS_IDLE) return;
if (active_dialog) {
substate = LSSUB_RELEASING;
ui->cb_line_state_changed();
ui->cb_stop_call_notification(line_number);
active_dialog->send_bye();
// If the line was part of a transfer with consultation,
// then clean the consultation state as the transfer cannot
// proceed anymore.
cleanup_transfer_consult_state();
cleanup();
return;
}
// Always send the CANCEL on the open dialog.
// The pending dialogs will be cleared when the INVITE gets
// terminated.
// CANCEL is send on the open dialog as the CANCEL must have
// the same tags as the INVITE.
if (open_dialog) {
substate = LSSUB_RELEASING;
ui->cb_line_state_changed();
ui->cb_stop_call_notification(line_number);
open_dialog->send_cancel(!pending_dialogs.empty());
// Make sure dialog is terminated if CANCEL glares with
// 2XX on INVITE.
for (list<t_dialog *>::iterator i = pending_dialogs.begin();
i != pending_dialogs.end(); i++)
{
(*i)->set_end_after_2xx_invite(true);
}
cleanup();
return;
}
// NOTE:
// The call is only ended for real when the dialog reaches
// the DS_TERMINATED state, i.e. a 200 OK on BYE is received
// or a 487 TERMINATED on INVITE is received.
}
void t_line::send_dtmf(char digit, bool inband, bool info) {
if (active_dialog && active_dialog->get_state() == DS_CONFIRMED) {
active_dialog->send_dtmf(digit, inband, info);
cleanup();
return;
}
}
void t_line::options(void) {
if (active_dialog && active_dialog->get_state() == DS_CONFIRMED) {
active_dialog->send_options();
cleanup();
return;
}
}
bool t_line::hold(bool rtponly) {
if (is_on_hold) return true;
if (active_dialog && active_dialog->get_state() == DS_CONFIRMED) {
active_dialog->hold(rtponly);
is_on_hold = true;
ui->cb_line_state_changed();
cleanup();
return true;
}
return false;
}
void t_line::retrieve(void) {
if (!is_on_hold) return;
if (active_dialog && active_dialog->get_state() == DS_CONFIRMED) {
active_dialog->retrieve();
is_on_hold = false;
ui->cb_line_state_changed();
cleanup();
return;
}
}
void t_line::kill_rtp(void) {
if (active_dialog) active_dialog->kill_rtp();
for (list<t_dialog *>::iterator i = pending_dialogs.begin();
i != pending_dialogs.end(); i++)
{
(*i)->kill_rtp();
}
for (list<t_dialog *>::iterator i = dying_dialogs.begin();
i != dying_dialogs.end(); i++)
{
(*i)->kill_rtp();
}
}
void t_line::refer(const t_url &uri, const string &display) {
if (active_dialog && active_dialog->get_state() == DS_CONFIRMED) {
active_dialog->send_refer(uri, display);
ui->cb_line_state_changed();
cleanup();
return;
}
}
void t_line::mute(bool enable) {
is_muted = enable;
}
void t_line::recvd_provisional(t_response *r, t_tuid tuid, t_tid tid) {
t_dialog *d;
if (active_dialog && active_dialog->match_response(r, 0)) {
active_dialog->recvd_response(r, tuid, tid);
cleanup();
return;
}
d = match_response(r, pending_dialogs);
if (d) {
d->recvd_response(r, tuid, tid);
cleanup();
return;
}
d = match_response(r, dying_dialogs);
if (d) {
d->recvd_response(r, tuid, tid);
cleanup();
return;
}
if (open_dialog && open_dialog->match_response(r, tuid)) {
if (r->hdr_cseq.method == INVITE) {
if (r->hdr_to.tag.size() > 0) {
// Create a new pending dialog
d = open_dialog->copy();
pending_dialogs.push_back(d);
d->recvd_response(r, tuid, tid);
} else {
open_dialog->recvd_response(r, tuid, tid);
}
} else {
open_dialog->recvd_response(r, tuid, tid);
}
cleanup();
return;
}
// out-of-dialog response
// Provisional responses should only be given for INVITE.
// A response for an INVITE is always in a dialog.
// Ignore provisional responses for other requests.
}
void t_line::recvd_success(t_response *r, t_tuid tuid, t_tid tid) {
t_dialog *d;
if (active_dialog && active_dialog->match_response(r, 0)) {
active_dialog->recvd_response(r, tuid, tid);
cleanup();
return;
}
d = match_response(r, pending_dialogs);
if (d) {
d->recvd_response(r, tuid, tid);
if (r->hdr_cseq.method == INVITE) {
if (!active_dialog) {
// Make the dialog the active dialog
active_dialog = d;
pending_dialogs.remove(d);
start_timer(LTMR_INVITE_COMP);
substate = LSSUB_ESTABLISHED;
ui->cb_line_state_changed();
} else {
// An active dialog already exists.
// Terminate this dialog by sending BYE
d->send_bye();
}
}
cleanup();
return;
}
d = match_response(r, dying_dialogs);
if (d) {
d->recvd_response(r, tuid, tid);
if (r->hdr_cseq.method == INVITE) {
d->send_bye();
}
cleanup();
return;
}
if (open_dialog && open_dialog->match_response(r, tuid)) {
if (r->hdr_cseq.method == INVITE) {
// Create a new dialog
d = open_dialog->copy();
if (!active_dialog) {
active_dialog = d;
active_dialog->recvd_response(r, tuid, tid);
start_timer(LTMR_INVITE_COMP);
substate = LSSUB_ESTABLISHED;
ui->cb_line_state_changed();
} else {
pending_dialogs.push_back(d);
d->recvd_response(r, tuid, tid);
// An active dialog already exists.
// Terminate this dialog by sending BYE
d->send_bye();
}
} else {
open_dialog->recvd_response(r, tuid, tid);
}
cleanup();
return;
}
// Response does not match with any pending request. Discard.
}
void t_line::recvd_redirect(t_response *r, t_tuid tuid, t_tid tid) {
t_dialog *d;
assert(user_config);
if (active_dialog) {
// If an active dialog exists then non-2XX should
// only be for this dialog.
if (active_dialog->match_response(r, 0)) {
// Redirection of mid-dialog request
if (!user_config->get_allow_redirection() ||
!active_dialog->redirect_request(r))
{
// Redirection not allowed/failed
active_dialog->recvd_response(r, tuid, tid);
}
// Retrieve a held line after a REFER failure
if (r->hdr_cseq.method == REFER &&
active_dialog->out_refer_req_failed)
{
active_dialog->out_refer_req_failed = false;
if (phone->get_active_line() == line_number &&
user_config->get_referrer_hold())
{
retrieve();
}
}
}
cleanup();
return;
}
d = match_response(r, pending_dialogs);
if (d) {
d->recvd_response(r, tuid, tid);
if (r->hdr_cseq.method == INVITE) {
pending_dialogs.remove(d);
MEMMAN_DELETE(d);
delete d;
// RFC 3261 13.2.2.3
// All early dialogs are considered terminated
// upon reception of the non-2xx final response.
list<t_dialog *>::iterator i;
for (i = pending_dialogs.begin();
i != pending_dialogs.end(); i++)
{
MEMMAN_DELETE(*i);
delete *i;
}
pending_dialogs.clear();
if (open_dialog) {
if (!user_config->get_allow_redirection() ||
!open_dialog->redirect_invite(r))
{
MEMMAN_DELETE(open_dialog);
delete open_dialog;
open_dialog = NULL;
}
}
}
cleanup();
return;
}
d = match_response(r, dying_dialogs);
if (d) {
d->recvd_response(r, tuid, tid);
cleanup();
return;
}
if (open_dialog && open_dialog->match_response(r, tuid)) {
if (r->hdr_cseq.method != INVITE) {
// TODO: can there be a non-INVITE response for an
// open dialog??
open_dialog->recvd_response(r, tuid, tid);
}
if (r->hdr_cseq.method == INVITE) {
if (!user_config->get_allow_redirection() ||
!open_dialog->redirect_invite(r))
{
// Redirection failed/not allowed
open_dialog->recvd_response(r, tuid, tid);
MEMMAN_DELETE(open_dialog);
delete open_dialog;
open_dialog = NULL;
}
// RFC 3261 13.2.2.3
// All early dialogs are considered terminated
// upon reception of the non-2xx final response.
list<t_dialog *>::iterator i;
for (i = pending_dialogs.begin();
i != pending_dialogs.end(); i++)
{
MEMMAN_DELETE(*i);
delete *i;
}
pending_dialogs.clear();
}
cleanup();
return;
}
// out-of-dialog responses should be handled by the phone
}
void t_line::recvd_client_error(t_response *r, t_tuid tuid, t_tid tid) {
t_dialog *d;
assert(user_config);
if (active_dialog) {
// If an active dialog exists then non-2XX should
// only be for this dialog.
if (active_dialog->match_response(r, 0)) {
bool response_processed = false;
if (r->must_authenticate()) {
// Authentication for mid-dialog request
if (active_dialog->resend_request_auth(r))
{
// Authorization successul.
// The response does not need to be
// processed any further
response_processed = true;
}
}
if (!response_processed) {
// The request failed, redirect it if there
// are other destinations available.
if (!user_config->get_allow_redirection() ||
!active_dialog->redirect_request(r))
{
// Request failed
active_dialog->
recvd_response(r, tuid, tid);
}
}
// Retrieve a held line after a REFER failure
if (r->hdr_cseq.method == REFER &&
active_dialog->out_refer_req_failed)
{
active_dialog->out_refer_req_failed = false;
if (phone->get_active_line() == line_number &&
user_config->get_referrer_hold())
{
retrieve();
}
}
}
cleanup();
return;
}
d = match_response(r, pending_dialogs);
if (d) {
if (r->hdr_cseq.method != INVITE) {
if (r->must_authenticate()) {
// Authentication for non-INVITE request in pending dialog
if (!d->resend_request_auth(r)) {
// Could not authorize, send response to dialog
// where it will be handle as a client failure.
d->recvd_response(r, tuid, tid);
}
} else {
d->recvd_response(r, tuid, tid);
}
} else {
d->recvd_response(r, tuid, tid);
pending_dialogs.remove(d);
MEMMAN_DELETE(d);
delete d;
// RFC 3261 13.2.2.3
// All early dialogs are considered terminated
// upon reception of the non-2xx final response.
list<t_dialog *>::iterator i;
for (i = pending_dialogs.begin();
i != pending_dialogs.end(); i++)
{
MEMMAN_DELETE(*i);
delete *i;
}
pending_dialogs.clear();
if (open_dialog) {
bool response_processed = false;
if (r->must_authenticate()) {
// INVITE authentication
if (open_dialog->resend_invite_auth(r))
{
// Authorization successul.
// The response does not need to
// be processed any further
response_processed = true;
}
}
// Resend INVITE if the response indicated that
// required extensions are not supported.
if (!response_processed &&
open_dialog->resend_invite_unsupported(r))
{
response_processed = true;
}
if (!response_processed) {
// The request failed, redirect it if there
// are other destinations available.
if (!user_config->get_allow_redirection() ||
!open_dialog->redirect_invite(r))
{
// Request failed
MEMMAN_DELETE(open_dialog);
delete open_dialog;
open_dialog = NULL;
}
}
}
}
cleanup();
return;
}
d = match_response(r, dying_dialogs);
if (d) {
d->recvd_response(r, tuid, tid);
cleanup();
return;
}
if (open_dialog && open_dialog->match_response(r, tuid)) {
// If the response is a 401/407 then do not send the
// response to the dialog as the request must be resent.
// For an INVITE request, the transaction layer has already
// sent ACK for a failure response.
if (r->hdr_cseq.method != INVITE) {
if (r->must_authenticate()) {
// Authenticate non-INVITE request
if (!open_dialog->resend_request_auth(r)) {
// Could not authorize, handle as other client
// errors.
open_dialog->recvd_response(r, tuid, tid);
}
} else {
open_dialog->recvd_response(r, tuid, tid);
}
}
if (r->hdr_cseq.method == INVITE) {
bool response_processed = false;
if (r->must_authenticate()) {
// INVITE authentication
if (open_dialog->resend_invite_auth(r))
{
// Authorization successul.
// The response does not need to
// be processed any further
response_processed = true;
}
}
// Resend INVITE if the response indicated that
// required extensions are not supported.
if (!response_processed &&
open_dialog->resend_invite_unsupported(r))
{
response_processed = true;
}
if (!response_processed) {
// The request failed, redirect it if there
// are other destinations available.
if (!user_config->get_allow_redirection() ||
!open_dialog->redirect_invite(r))
{
// Request failed
open_dialog->recvd_response(r, tuid, tid);
MEMMAN_DELETE(open_dialog);
delete open_dialog;
open_dialog = NULL;
}
}
// RFC 3261 13.2.2.3
// All early dialogs are considered terminated
// upon reception of the non-2xx final response.
list<t_dialog *>::iterator i;
for (i = pending_dialogs.begin();
i != pending_dialogs.end(); i++)
{
MEMMAN_DELETE(*i);
delete *i;
}
pending_dialogs.clear();
}
cleanup();
return;
}
// out-of-dialog responses should be handled by the phone
}
void t_line::recvd_server_error(t_response *r, t_tuid tuid, t_tid tid) {
t_dialog *d;
assert(user_config);
if (active_dialog) {
// If an active dialog exists then non-2XX should
// only be for this dialog.
if (active_dialog->match_response(r, 0)) {
bool response_processed = false;
if (r->code == R_503_SERVICE_UNAVAILABLE) {
// RFC 3263 4.3
// Failover to next destination
if (active_dialog->failover_request(r))
{
// Failover successul.
// The response does not need to be
// processed any further
response_processed = true;
}
}
if (!response_processed) {
// The request failed, redirect it if there
// are other destinations available.
if (!user_config->get_allow_redirection() ||
!active_dialog->redirect_request(r))
{
// Request failed
active_dialog->
recvd_response(r, tuid, tid);
}
}
// Retrieve a held line after a REFER failure
if (r->hdr_cseq.method == REFER &&
active_dialog->out_refer_req_failed)
{
active_dialog->out_refer_req_failed = false;
if (phone->get_active_line() == line_number &&
user_config->get_referrer_hold())
{
retrieve();
}
}
}
cleanup();
return;
}
d = match_response(r, pending_dialogs);
if (d) {
d->recvd_response(r, tuid, tid);
if (r->hdr_cseq.method == INVITE) {
pending_dialogs.remove(d);
MEMMAN_DELETE(d);
delete d;
// RFC 3261 13.2.2.3
// All early dialogs are considered terminated
// upon reception of the non-2xx final response.
list<t_dialog *>::iterator i;
for (i = pending_dialogs.begin();
i != pending_dialogs.end(); i++)
{
MEMMAN_DELETE(*i);
delete *i;
}
pending_dialogs.clear();
if (open_dialog) {
bool response_processed = false;
if (r->code == R_503_SERVICE_UNAVAILABLE) {
// INVITE failover
if (open_dialog->failover_invite())
{
// Failover successul.
// The response does not need to
// be processed any further
response_processed = true;
}
}
if (!response_processed) {
// The request failed, redirect it if there
// are other destinations available.
if (!user_config->get_allow_redirection() ||
!open_dialog->redirect_invite(r))
{
// Request failed
MEMMAN_DELETE(open_dialog);
delete open_dialog;
open_dialog = NULL;
}
}
}
}
cleanup();
return;
}
d = match_response(r, dying_dialogs);
if (d) {
d->recvd_response(r, tuid, tid);
cleanup();
return;
}
if (open_dialog && open_dialog->match_response(r, tuid)) {
// If the response is a 503 then do not send the
// response to the dialog as the request must be resent.
// For an INVITE request, the transaction layer has already
// sent ACK for a failure response.
if (r->code != R_503_SERVICE_UNAVAILABLE && r->hdr_cseq.method != INVITE) {
open_dialog->recvd_response(r, tuid, tid);
}
if (r->hdr_cseq.method == INVITE) {
bool response_processed = false;
if (r->code == R_503_SERVICE_UNAVAILABLE) {
// INVITE failover
if (open_dialog->failover_invite())
{
// Failover successul.
// The response does not need to
// be processed any further
response_processed = true;
}
}
if (!response_processed) {
// The request failed, redirect it if there
// are other destinations available.
if (!user_config->get_allow_redirection() ||
!open_dialog->redirect_invite(r))
{
// Request failed
open_dialog->recvd_response(r, tuid, tid);
MEMMAN_DELETE(open_dialog);
delete open_dialog;
open_dialog = NULL;
}
}
// RFC 3261 13.2.2.3
// All early dialogs are considered terminated
// upon reception of the non-2xx final response.
list<t_dialog *>::iterator i;
for (i = pending_dialogs.begin();
i != pending_dialogs.end(); i++)
{
MEMMAN_DELETE(*i);
delete *i;
}
pending_dialogs.clear();
}
cleanup();
return;
}
// out-of-dialog responses should be handled by the phone
}
void t_line::recvd_global_error(t_response *r, t_tuid tuid, t_tid tid) {
recvd_redirect(r, tuid, tid);
}
void t_line::recvd_invite(t_user *user, t_request *r, t_tid tid, const string &ringtone) {
t_response *resp;
switch (state) {
case LS_IDLE:
assert(!active_dialog);
assert(r->hdr_to.tag == "");
/*
// TEST ONLY
// Test code to test INVITE authentication
if (!r->hdr_authorization.is_populated()) {
resp = r->create_response(R_401_UNAUTHORIZED);
t_challenge c;
c.auth_scheme = AUTH_DIGEST;
c.digest_challenge.realm = "mtel.nl";
c.digest_challenge.nonce = "0123456789abcdef";
c.digest_challenge.opaque = "secret";
c.digest_challenge.algorithm = ALG_MD5;
c.digest_challenge.qop_options.push_back(QOP_AUTH);
c.digest_challenge.qop_options.push_back(QOP_AUTH_INT);
resp->hdr_www_authenticate.set_challenge(c);
send_response(resp, 0, tid);
return;
}
*/
assert(user);
user_config = user;
user_defined_ringtone = ringtone;
call_info.from_uri = r->hdr_from.uri;
call_info.from_display = r->hdr_from.display;
call_info.from_display_override = r->hdr_from.display_override;
if (r->hdr_organization.is_populated()) {
call_info.from_organization = r->hdr_organization.name;
} else {
call_info.from_organization.clear();
}
call_info.to_uri = r->hdr_to.uri;
call_info.to_display = r->hdr_to.display;
call_info.to_organization.clear();
call_info.subject = r->hdr_subject.subject;
try_to_encrypt = user_config->get_zrtp_enabled();
// 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))
{
call_info.refer_supported = true;
}
active_dialog = new t_dialog(this);
MEMMAN_NEW(active_dialog);
active_dialog->recvd_request(r, 0, tid);
state = LS_BUSY;
substate = LSSUB_INCOMING_PROGRESS;
ui->cb_line_state_changed();
start_timer(LTMR_NO_ANSWER);
cleanup();
// Answer if auto answer mode is activated
if (auto_answer) {
// Validate speaker and mic
string error_msg;
if (!sys_config->exec_audio_validation(false, true, true, error_msg)) {
ui->cb_display_msg(error_msg, MSG_CRITICAL);
} else {
answer();
}
}
break;
case LS_BUSY:
// Only re-INVITEs can be sent to a busy line
assert(r->hdr_to.tag != "");
/*
// TEST ONLY
// Test code to test re-INVITE authentication
if (!r->hdr_authorization.is_populated()) {
resp = r->create_response(R_401_UNAUTHORIZED);
t_challenge c;
c.auth_scheme = AUTH_DIGEST;
c.digest_challenge.realm = "mtel.nl";
c.digest_challenge.nonce = "0123456789abcdef";
c.digest_challenge.opaque = "secret";
c.digest_challenge.algorithm = ALG_MD5;
c.digest_challenge.qop_options.push_back(QOP_AUTH);
c.digest_challenge.qop_options.push_back(QOP_AUTH_INT);
resp->hdr_www_authenticate.set_challenge(c);
send_response(resp, 0, tid);
return;
}
*/
if (active_dialog && active_dialog->match_request(r)) {
// re-INVITE
active_dialog->recvd_request(r, 0, tid);
cleanup();
return;
}
// Should not get here as phone already checked that
// the request matched with this line
assert(false);
break;
default:
assert(false);
}
}
void t_line::recvd_ack(t_request *r, t_tid tid) {
if (active_dialog && active_dialog->match_request(r)) {
active_dialog->recvd_request(r, 0, tid);
substate = LSSUB_ESTABLISHED;
ui->cb_line_state_changed();
} else {
// Should not get here as phone already checked that
// the request matched with this line
assert(false);
}
cleanup();
}
void t_line::recvd_cancel(t_request *r, t_tid cancel_tid,
t_tid target_tid)
{
// A CANCEL matches a dialog if the target tid equals the tid
// of the INVITE request. This will be checked by
// dialog::recvd_cancel() itself.
if (active_dialog) {
active_dialog->recvd_cancel(r, cancel_tid, target_tid);
} else {
// Should not get here as phone already checked that
// the request matched with this line
assert(false);
}
cleanup();
}
void t_line::recvd_bye(t_request *r, t_tid tid) {
if (active_dialog && active_dialog->match_request(r)) {
active_dialog->recvd_request(r, 0, tid);
} else {
// Should not get here as phone already checked that
// the request matched with this line
assert(false);
}
cleanup();
}
void t_line::recvd_options(t_request *r, t_tid tid) {
if (active_dialog && active_dialog->match_request(r)) {
active_dialog->recvd_request(r, 0, tid);
} else {
// Should not get here as phone already checked that
// the request matched with this line
assert(false);
}
cleanup();
}
void t_line::recvd_prack(t_request *r, t_tid tid) {
if (active_dialog && active_dialog->match_request(r)) {
active_dialog->recvd_request(r, 0, tid);
} else {
// Should not get here as phone already checked that
// the request matched with this line
assert(false);
}
cleanup();
}
void t_line::recvd_subscribe(t_request *r, t_tid tid) {
if (active_dialog && active_dialog->match_request(r)) {
active_dialog->recvd_request(r, 0, tid);
} else {
// Should not get here as phone already checked that
// the request matched with this line
assert(false);
}
cleanup();
}
void t_line::recvd_notify(t_request *r, t_tid tid) {
if (active_dialog && active_dialog->match_request(r)) {
active_dialog->recvd_request(r, 0, tid);
} else {
// Should not get here as phone already checked that
// the request matched with this line
assert(false);
}
cleanup();
}
void t_line::recvd_info(t_request *r, t_tid tid) {
if (active_dialog && active_dialog->match_request(r)) {
active_dialog->recvd_request(r, 0, tid);
} else {
// Should not get here as phone already checked that
// the request matched with this line
assert(false);
}
cleanup();
}
void t_line::recvd_message(t_request *r, t_tid tid) {
if (active_dialog && active_dialog->match_request(r)) {
active_dialog->recvd_request(r, 0, tid);
} else {
// Should not get here as phone already checked that
// the request matched with this line
assert(false);
}
cleanup();
}
bool t_line::recvd_refer(t_request *r, t_tid tid) {
bool retval = false;
if (active_dialog && active_dialog->match_request(r)) {
active_dialog->recvd_request(r, 0, tid);
retval = active_dialog->refer_accepted;
} else {
// Should not get here as phone already checked that
// the request matched with this line
assert(false);
}
cleanup();
return retval;
}
void t_line::recvd_refer_permission(bool permission, t_request *r) {
if (active_dialog && active_dialog->match_request(r)) {
active_dialog->recvd_refer_permission(permission, r);
}
cleanup();
}
void t_line::recvd_stun_resp(StunMessage *r, t_tuid tuid, t_tid tid) {
t_dialog *d;
if (active_dialog && active_dialog->match_response(r, tuid)) {
active_dialog->recvd_stun_resp(r, tuid, tid);
cleanup();
return;
}
if (open_dialog && open_dialog->match_response(r, tuid)) {
open_dialog->recvd_stun_resp(r, tuid, tid);
cleanup();
return;
}
d = match_response(r, tuid, pending_dialogs);
if (d) {
d->recvd_stun_resp(r, tuid, tid);
cleanup();
return;
}
d = match_response(r, tuid, dying_dialogs);
if (d) {
d->recvd_stun_resp(r, tuid, tid);
cleanup();
return;
}
}
void t_line::failure(t_failure failure, t_tid tid) {
// TODO
}
void t_line::timeout(t_line_timer timer, t_object_id did) {
t_dialog *dialog = get_dialog(did);
list<t_display_url> cf_dest; // call forwarding destinations
switch (timer) {
case LTMR_ACK_TIMEOUT:
// If there is no dialog then ignore the timeout
if (dialog) {
dialog->id_ack_timeout = 0;
dialog->timeout(timer);
}
break;
case LTMR_ACK_GUARD:
// If there is no dialog then ignore the timeout
if (dialog) {
dialog->id_ack_guard = 0;
dialog->dur_ack_timeout = 0;
dialog->timeout(timer);
}
break;
case LTMR_INVITE_COMP:
id_invite_comp = 0;
// RFC 3261 13.2.2.4
// The UAC core considers the INVITE transaction completed
// 64*T1 seconds after the reception of the first 2XX
// response.
// Cleanup all open and pending dialogs
cleanup_open_pending();
break;
case LTMR_NO_ANSWER:
// User did not answer the call.
// Reject call or redirect it if CF_NOANSWER is active.
// If there is no active dialog then ignore the timeout.
// The timer should have been stopped already.
log_file->write_report("No answer timeout",
"t_line::timeout");
if (active_dialog) {
assert(user_config);
t_service *srv = phone->ref_service(user_config);
if (srv->get_cf_active(CF_NOANSWER, cf_dest)) {
log_file->write_report("Call redirection no answer",
"t_line::timeout");
active_dialog->redirect(cf_dest,
R_302_MOVED_TEMPORARILY);
} else {
active_dialog->reject(R_480_TEMP_NOT_AVAILABLE,
REASON_480_NO_ANSWER);
}
ui->cb_answer_timeout(get_line_number());
}
break;
case LTMR_RE_INVITE_GUARD:
// If there is no dialog then ignore the timeout
if (dialog) {
dialog->id_re_invite_guard = 0;
dialog->timeout(timer);
}
break;
case LTMR_GLARE_RETRY:
// If there is no dialog then ignore the timeout
if (dialog) {
dialog->id_glare_retry = 0;
dialog->timeout(timer);
}
break;
case LTMR_100REL_TIMEOUT:
// If there is no dialog then ignore the timeout
if (dialog) {
dialog->id_100rel_timeout = 0;
dialog->timeout(timer);
}
break;
case LTMR_100REL_GUARD:
// If there is no dialog then ignore the timeout
if (dialog) {
dialog->id_100rel_guard = 0;
dialog->dur_100rel_timeout = 0;
dialog->timeout(timer);
}
break;
case LTMR_CANCEL_GUARD:
// If there is no dialog then ignore the timeout
if (dialog) {
dialog->id_cancel_guard = 0;
dialog->timeout(timer);
}
break;
default:
assert(false);
}
cleanup();
}
void t_line::timeout_sub(t_subscribe_timer timer, t_object_id did,
const string &event_type, const string &event_id)
{
t_dialog *dialog = get_dialog(did);
if (dialog) dialog->timeout_sub(timer, event_type, event_id);
cleanup();
}
bool t_line::match(t_response *r, t_tuid tuid) const {
if (open_dialog && open_dialog->match_response(r, tuid)) {
return true;
}
if (active_dialog && active_dialog->match_response(r, 0)) {
return true;
}
if (match_response(r, pending_dialogs)) {
return true;
}
if (match_response(r, dying_dialogs)) {
return true;
}
return false;
}
bool t_line::match(t_request *r) const {
assert(r->method != CANCEL);
return (active_dialog && active_dialog->match_request(r));
}
bool t_line::match_cancel(t_request *r, t_tid target_tid) const {
assert(r->method == CANCEL);
// A CANCEL matches a dialog if the target tid equals the tid
// of the INVITE request.
return (active_dialog && active_dialog->match_cancel(r, target_tid));
}
bool t_line::match(StunMessage *r, t_tuid tuid) const {
if (open_dialog && open_dialog->match_response(r, tuid)) {
return true;
}
if (active_dialog && active_dialog->match_response(r, tuid)) {
return true;
}
if (match_response(r, tuid, pending_dialogs)) {
return true;
}
if (match_response(r, tuid, dying_dialogs)) {
return true;
}
return false;
}
bool t_line::match_replaces(const string &call_id, const string &to_tag,
const string &from_tag, bool &early_matched) const
{
if (active_dialog && active_dialog->match(call_id, to_tag, from_tag)) {
early_matched = false;
return true;
}
// RFC 3891 3
// And early dialog only matches when it was created by the UA
t_dialog *d;
if ((d = match_call_id_tags(call_id, to_tag, from_tag,
pending_dialogs)) != NULL && d->is_call_id_owner())
{
early_matched = true;
return true;
}
return false;
}
bool t_line::is_invite_retrans(t_request *r) {
assert(r->method == INVITE);
return (active_dialog && active_dialog->is_invite_retrans(r));
}
void t_line::process_invite_retrans(void) {
if (active_dialog) active_dialog->process_invite_retrans();
}
string t_line::create_user_contact(void) const {
assert(user_config);
return user_config->create_user_contact(hide_user);
}
string t_line::create_user_uri(void) const {
assert(user_config);
return user_config->create_user_uri(hide_user);
}
t_response *t_line::create_options_response(t_request *r, bool in_dialog) const
{
assert(user_config);
return phone->create_options_response(user_config, r, in_dialog);
}
void t_line::send_response(t_response *r, t_tuid tuid, t_tid tid) {
if (hide_user) {
r->hdr_privacy.add_privacy(PRIVACY_ID);
}
phone->send_response(r, tuid, tid);
}
void t_line::send_request(t_request *r, t_tuid tuid) {
assert(user_config);
phone->send_request(user_config, r, tuid);
}
t_phone *t_line::get_phone(void) const {
return phone;
}
unsigned short t_line::get_line_number(void) const {
return line_number;
}
bool t_line::get_is_on_hold(void) const {
return is_on_hold;
}
bool t_line::get_is_muted(void) const {
return is_muted;
}
bool t_line::get_hide_user(void) const {
return hide_user;
}
bool t_line::get_is_transfer_consult(unsigned short &lineno) const {
lineno = consult_transfer_from_line;
return is_transfer_consult;
}
void t_line::set_is_transfer_consult(bool enable, unsigned short lineno) {
is_transfer_consult = enable;
consult_transfer_from_line = lineno;
}
bool t_line::get_to_be_transferred(unsigned short &lineno) const {
lineno = consult_transfer_to_line;
return to_be_transferred;
}
void t_line::set_to_be_transferred(bool enable, unsigned short lineno) {
to_be_transferred = enable;
consult_transfer_to_line = lineno;
}
bool t_line::get_is_encrypted(void) const {
t_audio_session *as = get_audio_session();
if (as) return as->get_is_encrypted();
return false;
}
bool t_line::get_try_to_encrypt(void) const {
return try_to_encrypt;
}
bool t_line::get_auto_answer(void) const {
return auto_answer;
}
void t_line::set_auto_answer(bool enable) {
auto_answer = enable;
}
bool t_line::is_refer_succeeded(void) const {
if (active_dialog) return active_dialog->refer_succeeded;
return false;
}
bool t_line::has_media(void) const {
t_session *session = get_session();
return (session && !session->receive_host.empty() && !session->dst_rtp_host.empty());
}
t_url t_line::get_remote_target_uri(void) const {
if (!active_dialog) return t_url();
return active_dialog->get_remote_target_uri();
}
string t_line::get_remote_target_display(void) const {
if (!active_dialog) return "";
return active_dialog->get_remote_target_display();
}
t_url t_line::get_remote_uri(void) const {
if (!active_dialog) return t_url();
return active_dialog->get_remote_uri();
}
string t_line::get_remote_display(void) const {
if (!active_dialog) return "";
return active_dialog->get_remote_display();
}
string t_line::get_call_id(void) const {
if (!active_dialog) return "";
return active_dialog->get_call_id();
}
string t_line::get_local_tag(void) const {
if (!active_dialog) return "";
return active_dialog->get_local_tag();
}
string t_line::get_remote_tag(void) const {
if (!active_dialog) return "";
return active_dialog->get_remote_tag();
}
bool t_line::remote_extension_supported(const string &extension) const {
if (!active_dialog) return false;
return active_dialog->remote_extension_supported(extension);
}
bool t_line::seize(void) {
// Only an idle line can be seized.
if (substate != LSSUB_IDLE) return false;
substate = LSSUB_SEIZED;
ui->cb_line_state_changed();
return true;
}
void t_line::unseize(void) {
// Only a seized line can be unseized.
if (substate != LSSUB_SEIZED) return;
substate = LSSUB_IDLE;
ui->cb_line_state_changed();
}
t_session *t_line::get_session(void) const {
if (!active_dialog) return NULL;
return active_dialog->get_session();
}
t_audio_session *t_line::get_audio_session(void) const {
if (!active_dialog) return NULL;
return active_dialog->get_audio_session();
}
void t_line::notify_refer_progress(t_response *r) {
if (active_dialog) active_dialog->notify_refer_progress(r);
}
void t_line::failed_retrieve(void) {
// Call retrieve failed, so line is still on-hold
is_on_hold = true;
ui->cb_line_state_changed();
}
void t_line::failed_hold(void) {
// Call hold failed, so line is not on-hold
is_on_hold = false;
ui->cb_line_state_changed();
}
void t_line::retry_retrieve_succeeded(void) {
// Retry of retrieve succeeded, so line is not on-hold anymore
is_on_hold = false;
ui->cb_line_state_changed();
}
t_call_info t_line::get_call_info(void) const {
return call_info;
}
void t_line::ci_set_dtmf_supported(bool supported, bool inband, bool info) {
call_info.dtmf_supported = supported;
call_info.dtmf_inband = inband;
call_info.dtmf_info = info;
}
void t_line::ci_set_last_provisional_reason(const string &reason) {
call_info.last_provisional_reason = reason;
}
void t_line::ci_set_send_codec(t_audio_codec codec) {
call_info.send_codec = codec;
}
void t_line::ci_set_recv_codec(t_audio_codec codec) {
call_info.recv_codec = codec;
}
void t_line::ci_set_refer_supported(bool supported) {
call_info.refer_supported = supported;
}
void t_line::init_rtp_port(void) {
rtp_port = sys_config->get_rtp_port() + line_number * 2;
}
unsigned short t_line::get_rtp_port(void) const {
return rtp_port;
}
t_user *t_line::get_user(void) const {
return user_config;
}
string t_line::get_ringtone(void) const {
if (!user_defined_ringtone.empty()) {
// Ring tone returned by incoming call script
return user_defined_ringtone;
} else if (!user_config->get_ringtone_file().empty()) {
// Ring tone from user profile
return user_config->get_ringtone_file();
} else if (!sys_config->get_ringtone_file().empty()) {
// Ring tone from system settings
return sys_config->get_ringtone_file();
} else {
// Twinkle default
return FILE_RINGTONE;
}
}
void t_line::confirm_zrtp_sas(void) {
t_audio_session *as = get_audio_session();
if (as && !as->get_zrtp_sas_confirmed()) {
as->confirm_zrtp_sas();
ui->cb_zrtp_sas_confirmed(line_number);
ui->cb_line_state_changed();
log_file->write_header("t_line::confirm_zrtp_sas");
log_file->write_raw("Line ");
log_file->write_raw(line_number + 1);
log_file->write_raw(": User confirmed ZRTP SAS\n");
log_file->write_footer();
}
}
void t_line::reset_zrtp_sas_confirmation(void) {
t_audio_session *as = get_audio_session();
if (as && as->get_zrtp_sas_confirmed()) {
as->reset_zrtp_sas_confirmation();
ui->cb_zrtp_sas_confirmation_reset(line_number);
ui->cb_line_state_changed();
log_file->write_header("t_line::reset_zrtp_sas_confirmation");
log_file->write_raw("Line ");
log_file->write_raw(line_number + 1);
log_file->write_raw(": User reset ZRTP SAS confirmation\n");
log_file->write_footer();
}
}
void t_line::enable_zrtp(void) {
t_audio_session *as = get_audio_session();
if (as) {
as->enable_zrtp();
}
}
void t_line::zrtp_request_go_clear(void) {
t_audio_session *as = get_audio_session();
if (as) {
as->zrtp_request_go_clear();
}
}
void t_line::zrtp_go_clear_ok(void) {
t_audio_session *as = get_audio_session();
if (as) {
as->zrtp_go_clear_ok();
}
}
void t_line::force_idle(void) {
cleanup_forced();
}
void t_line::set_keep_seized(bool seize) {
keep_seized = seize;
cleanup();
}
bool t_line::get_keep_seized(void) const {
return keep_seized;
}
t_dialog *t_line::get_dialog_with_active_session(void) const {
if (open_dialog && open_dialog->has_active_session()) {
return open_dialog;
}
if (active_dialog && active_dialog->has_active_session()) {
return active_dialog;
}
for (list<t_dialog *>::const_iterator it = pending_dialogs.begin();
it != pending_dialogs.end(); ++it)
{
if ((*it)->has_active_session()) {
return *it;
}
}
for (list<t_dialog *>::const_iterator it = dying_dialogs.begin();
it != dying_dialogs.end(); ++it)
{
if ((*it)->has_active_session()) {
return *it;
}
}
return NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1