/*
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 <iostream>
#include <cstdlib>
#include "address_book.h"
#include "events.h"
#include "line.h"
#include "log.h"
#include "sys_settings.h"
#include "translator.h"
#include "userintf.h"
#include "util.h"
#include "user.h"
#include "audio/rtp_telephone_event.h"
#include "parser/parse_ctrl.h"
#include "sockets/interfaces.h"
#include "audits/memman.h"
#define CLI_PROMPT "Twinkle> "
extern string user_host;
extern t_event_queue *evq_trans_layer;
/////////////////////////////
// Private
/////////////////////////////
string t_userintf::expand_destination(t_user *user_config, const string &dst) {
assert(user_config);
string s = dst;
// Add domain if missing
if (s.find('@') == string::npos) {
// Remove white space
s = remove_white_space(s);
// Remove special phone symbols
if (user_config->get_remove_special_phone_symbols() &&
looks_like_phone(s, user_config->get_special_phone_symbols()))
{
s = remove_symbols(s, user_config->get_special_phone_symbols());
}
// Convert number according to the number conversion rules
s = user_config->convert_number(s);
s += '@';
s += user_config->get_domain();
}
// Add sip-scheme if missing
if (s.substr(0, 4) != "sip:") {
s = "sip:" + s;
}
// RFC 3261 19.1.1
// Add user=phone for telehpone numbers
// If the URI contains a telephone number it SHOULD contain
// the user=phone parameter.
if (user_config->get_numerical_user_is_phone()) {
t_url u(s);
if (u.get_user_param().empty() &&
u.user_looks_like_phone(user_config->get_special_phone_symbols())) {
s += ";user=phone";
}
}
return s;
}
void t_userintf::expand_destination(t_user *user_config,
const string &dst, string &display, string &dst_url)
{
display.clear();
dst_url.clear();
if (dst.empty()) {
return;
}
// If there is a display name then the url part is between angle
// brackets.
if (dst[dst.size() - 1] != '>') {
dst_url = expand_destination(user_config, dst);
return;
}
// Find start of url
string::size_type i = dst.rfind('<');
if (i == string::npos) {
// It seems the string is invalid.
return;
}
dst_url = expand_destination(user_config, dst.substr(i + 1, dst.size() - i - 2));
if (i > 0) {
display = unquote(trim(dst.substr(0, i)));
}
}
void t_userintf::expand_destination(t_user *user_config,
const string &dst, t_display_url &display_url)
{
string url_str;
expand_destination(user_config, dst, display_url.display, url_str);
display_url.url.set_url(url_str);
}
void t_userintf::expand_destination(t_user *user_config,
const string &dst, t_display_url &display_url, string &subject,
string &dst_no_headers)
{
string headers;
dst_no_headers = dst;
t_url u(dst);
// Split headers from URI
if (u.is_valid()) {
// destination is a valid URI. Strip off the headers if any
headers = u.get_headers();
// Cut off headers
// Note that a separator (?) will be in front of the
// headers string
if (!headers.empty()) {
int i = dst.find(headers);
if (i != string::npos) {
dst_no_headers = dst.substr(0, i - 1);
}
}
expand_destination(user_config, dst_no_headers, display_url);
} else {
// destination may be a short URI.
// Split at a '?' to find any headers.
// NOTE: this is not fool proof. A user name may contain a '?'
vector<string> l = split_on_first(dst, '?');
dst_no_headers = l[0];
expand_destination(user_config, dst_no_headers, display_url);
if (display_url.is_valid() && l.size() == 2) {
headers = l[1];
}
}
// Parse headers to find subject header
subject.clear();
if (!headers.empty()) {
try {
t_sip_message *m = t_parser::parse_headers(headers);
if (m->hdr_subject.is_populated()) {
subject = m->hdr_subject.subject;
}
MEMMAN_DELETE(m);
delete m;
} catch (int) {
// ignore invalid headers
}
}
}
bool t_userintf::parse_args(const list<string> command_list,
list<t_command_arg> &al)
{
t_command_arg arg;
bool parsed_flag = false;
al.clear();
arg.flag = 0;
arg.value = "";
for (list<string>::const_iterator i = command_list.begin();
i != command_list.end(); i++)
{
if (i == command_list.begin()) continue;
const string &s = *i;
if (s[0] == '-') {
if (s.size() == 1) return false;
if (parsed_flag) al.push_back(arg);
arg.flag = s[1];
if (s.size() > 2) {
arg.value = unquote(s.substr(2));
al.push_back(arg);
arg.flag = 0;
arg.value = "";
parsed_flag = false;
} else {
arg.value = "";
parsed_flag = true;
}
} else {
if (parsed_flag) {
arg.value = unquote(s);
} else {
arg.flag = 0;
arg.value = unquote(s);
}
al.push_back(arg);
parsed_flag = false;
arg.flag = 0;
arg.value = "";
}
}
// Last parsed argument was a flag only
if (parsed_flag) al.push_back(arg);
return true;
}
bool t_userintf::exec_invite(const list<string> command_list, bool immediate) {
list<t_command_arg> al;
string display;
string subject;
string destination;
bool hide_user = false;
if (!parse_args(command_list, al)) {
exec_command("help call");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 'd':
display = i->value;
break;
case 's':
subject = i->value;
break;
case 'h':
hide_user = true;
break;
case 0:
destination = i->value;
break;
default:
exec_command("help call");
return false;
break;
}
}
return do_invite(destination, display, subject, immediate, hide_user);
}
bool t_userintf::do_invite(const string &destination, const string &display,
const string &subject, bool immediate, bool anonymous)
{
t_url dest_url(expand_destination(active_user, destination));
if (!dest_url.is_valid()) {
exec_command("help call");
return false;
}
t_url vm_url(expand_destination(active_user, active_user->get_mwi_vm_address()));
if (dest_url != vm_url) {
// Keep call information for redial
last_called_url = dest_url;
last_called_display = display;
last_called_subject = subject;
last_called_profile = active_user->get_profile_name();
last_called_hide_user = anonymous;
}
phone->pub_invite(active_user, dest_url, display, subject, anonymous);
return true;
}
bool t_userintf::exec_redial(const list<string> command_list) {
if (can_redial()) {
do_redial();
return true;
}
return false;
}
void t_userintf::do_redial(void) {
t_user *user_config = phone->ref_user_profile(last_called_profile);
phone->pub_invite(user_config, last_called_url, last_called_display,
last_called_subject, last_called_hide_user);
}
bool t_userintf::exec_answer(const list<string> command_list) {
do_answer();
return true;
}
void t_userintf::do_answer(void) {
cb_stop_call_notification(phone->get_active_line());
phone->pub_answer();
}
bool t_userintf::exec_answerbye(const list<string> command_list) {
do_answerbye();
return true;
}
void t_userintf::do_answerbye(void) {
unsigned short line = phone->get_active_line();
switch (phone->get_line_substate(line)) {
case LSSUB_INCOMING_PROGRESS:
do_answer();
break;
case LSSUB_OUTGOING_PROGRESS:
case LSSUB_ESTABLISHED:
do_bye();
break;
}
}
bool t_userintf::exec_reject(const list<string> command_list) {
do_reject();
return true;
}
void t_userintf::do_reject(void) {
cb_stop_call_notification(phone->get_active_line());
phone->pub_reject();
cout << endl;
cout << "Line " << phone->get_active_line() + 1 << ": call rejected.\n";
cout << endl;
}
bool t_userintf::exec_redirect(const list<string> command_list, bool immediate) {
list<t_command_arg> al;
list<string> dest_list;
int num_redirections = 0;
bool show_status = false;
bool action_present = false;
bool enable = true;
bool type_present = false;
t_cf_type cf_type;
if (!parse_args(command_list, al)) {
exec_command("help redirect");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 's':
show_status = true;
break;
case 't':
if (i->value == "always") {
cf_type = CF_ALWAYS;
} else if (i->value == "busy") {
cf_type = CF_BUSY;
} else if (i->value == "noanswer") {
cf_type = CF_NOANSWER;
} else {
exec_command("help redirect");
return false;
}
type_present = true;
break;
case 'a':
if (i->value == "on") {
enable = true;
} else if (i->value == "off") {
enable = false;
} else {
exec_command("help redirect");
return false;
}
action_present = true;
break;
case 0:
dest_list.push_back(i->value);
num_redirections++;
break;
default:
exec_command("help redirect");
return false;
break;
}
}
if (type_present && enable && (num_redirections == 0 || num_redirections > 5)) {
exec_command("help redirect");
return false;
}
if (!type_present && action_present && enable) {
exec_command("help redirect");
return false;
}
if (!type_present && !action_present &&
(num_redirections == 0 || num_redirections > 5))
{
exec_command("help redirect");
return false;
}
do_redirect(show_status, type_present, cf_type, action_present, enable,
num_redirections, dest_list, immediate);
return true;
}
void t_userintf::do_redirect(bool show_status, bool type_present, t_cf_type cf_type,
bool action_present, bool enable, int num_redirections,
const list<string> &dest_strlist, bool immediate)
{
list<t_display_url> dest_list;
for (list<string>::const_iterator i = dest_strlist.begin();
i != dest_strlist.end(); i++)
{
t_display_url du;
du.url = expand_destination(active_user, *i);
du.display.clear();
if (!du.is_valid()) return;
dest_list.push_back(du);
}
if (show_status) {
list<t_display_url> cf_dest; // call forwarding destinations
cout << endl;
cout << "Redirect always: ";
if (phone->ref_service(active_user)->get_cf_active(CF_ALWAYS, cf_dest)) {
for (list<t_display_url>::iterator i = cf_dest.begin();
i != cf_dest.end(); i++)
{
if (i != cf_dest.begin()) cout << ", ";
cout << i->encode();
}
} else {
cout << "not active";
}
cout << endl;
cout << "Redirect busy: ";
if (phone->ref_service(active_user)->get_cf_active(CF_BUSY, cf_dest)) {
for (list<t_display_url>::iterator i = cf_dest.begin();
i != cf_dest.end(); i++)
{
if (i != cf_dest.begin()) cout << ", ";
cout << i->encode();
}
} else {
cout << "not active";
}
cout << endl;
cout << "Redirect noanswer: ";
if (phone->ref_service(active_user)->get_cf_active(CF_NOANSWER, cf_dest)) {
for (list<t_display_url>::iterator i = cf_dest.begin();
i != cf_dest.end(); i++)
{
if (i != cf_dest.begin()) cout << ", ";
cout << i->encode();
}
} else {
cout << "not active";
}
cout << endl;
cout << endl;
return;
}
// Enable/disable permanent redirections
if (type_present) {
if (enable) {
phone->ref_service(active_user)->enable_cf(cf_type, dest_list);
cout << "Redirection enabled.\n\n";
} else {
phone->ref_service(active_user)->disable_cf(cf_type);
cout << "Redirection disabled.\n\n";
}
return;
} else {
if (action_present) {
if (!enable) {
phone->ref_service(active_user)->disable_cf(CF_ALWAYS);
phone->ref_service(active_user)->disable_cf(CF_BUSY);
phone->ref_service(active_user)->disable_cf(CF_NOANSWER);
cout << "All redirections disabled.\n\n";
return;
}
return;
}
}
// Redirect current call
cb_stop_call_notification(phone->get_active_line());
phone->pub_redirect(dest_list, 302);
cout << endl;
cout << "Line " << phone->get_active_line() + 1 << ": call redirected.\n";
cout << endl;
}
bool t_userintf::exec_dnd(const list<string> command_list) {
list<t_command_arg> al;
bool show_status = false;
bool toggle = true;
bool enable;
if (!parse_args(command_list, al)) {
exec_command("help dnd");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 's':
show_status = true;
break;
case 'a':
if (i->value == "on") {
enable = true;
} else if (i->value == "off") {
enable = false;
} else {
exec_command("help dnd");
return false;
}
toggle = false;
break;
default:
exec_command("help dnd");
return false;
break;
}
}
do_dnd(show_status, toggle, enable);
return true;
}
void t_userintf::do_dnd(bool show_status, bool toggle, bool enable) {
if (show_status) {
cout << endl;
cout << "Do not disturb: ";
if (phone->ref_service(active_user)->is_dnd_active()) {
cout << "active";
} else {
cout << "not active";
}
cout << endl;
return;
}
if (toggle) {
enable = !phone->ref_service(active_user)->is_dnd_active();
}
if (enable) {
phone->ref_service(active_user)->enable_dnd();
cout << "Do not disturb enabled.\n\n";
return;
} else {
phone->ref_service(active_user)->disable_dnd();
cout << "Do not disturb disabled.\n\n";
return;
}
}
bool t_userintf::exec_auto_answer(const list<string> command_list) {
list<t_command_arg> al;
bool show_status = false;
bool toggle = true;
bool enable;
if (!parse_args(command_list, al)) {
exec_command("help auto_answer");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 's':
show_status = true;
break;
case 'a':
if (i->value == "on") {
enable = true;
} else if (i->value == "off") {
enable = false;
} else {
exec_command("help auto_answer");
return false;
}
toggle = false;
break;
default:
exec_command("help auto_answer");
return false;
break;
}
}
do_auto_answer(show_status, toggle, enable);
return true;
}
void t_userintf::do_auto_answer(bool show_status, bool toggle, bool enable) {
if (show_status) {
cout << endl;
cout << "Auto answer: ";
if (phone->ref_service(active_user)->is_auto_answer_active()) {
cout << "active";
} else {
cout << "not active";
}
cout << endl;
return;
}
if (toggle) {
enable = !phone->ref_service(active_user)->is_auto_answer_active();
}
if (enable) {
phone->ref_service(active_user)->enable_auto_answer(true);
cout << "Auto answer enabled.\n\n";
return;
} else {
phone->ref_service(active_user)->enable_auto_answer(false);
cout << "Auto answer disabled.\n\n";
return;
}
}
bool t_userintf::exec_bye(const list<string> command_list) {
do_bye();
return true;
}
void t_userintf::do_bye(void) {
phone->pub_end_call();
}
bool t_userintf::exec_hold(const list<string> command_list) {
do_hold();
return true;
}
void t_userintf::do_hold(void) {
phone->pub_hold();
}
bool t_userintf::exec_retrieve(const list<string> command_list) {
do_retrieve();
return true;
}
void t_userintf::do_retrieve(void) {
phone->pub_retrieve();
}
bool t_userintf::exec_refer(const list<string> command_list, bool immediate) {
list<t_command_arg> al;
string destination;
bool dest_set = false;
t_transfer_type transfer_type = TRANSFER_BASIC;
if (!parse_args(command_list, al)) {
exec_command("help transfer");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 'c':
if (transfer_type != TRANSFER_BASIC) {
exec_command("help transfer");
return false;
}
transfer_type = TRANSFER_CONSULT;
if (!i->value.empty()) {
destination = i->value;
dest_set = true;
}
break;
case 'l':
if (transfer_type != TRANSFER_BASIC) {
exec_command("help transfer");
return false;
}
transfer_type = TRANSFER_OTHER_LINE;
break;
case 0:
destination = i->value;
dest_set = true;
break;
default:
exec_command("help transfer");
return false;
break;
}
}
if (!dest_set && transfer_type == TRANSFER_BASIC) {
exec_command("help transfer");
return false;
}
return do_refer(destination, transfer_type, immediate);
}
bool t_userintf::do_refer(const string &destination, t_transfer_type transfer_type,
bool immediate)
{
t_url dest_url;
if (transfer_type == TRANSFER_BASIC ||
(transfer_type == TRANSFER_CONSULT && !destination.empty()))
{
dest_url.set_url(expand_destination(active_user, destination));
if (!dest_url.is_valid()) {
exec_command("help transfer");
return false;
}
}
unsigned short active_line;
unsigned short other_line;
unsigned short line_to_be_transferred;
switch (transfer_type) {
case TRANSFER_BASIC:
phone->pub_refer(dest_url, "");
break;
case TRANSFER_CONSULT:
if (destination.empty()) {
active_line = phone->get_active_line();
if (!phone->is_line_transfer_consult(active_line,
line_to_be_transferred))
{
// There is no call to transfer
return false;
}
phone->pub_refer(line_to_be_transferred, active_line);
} else {
phone->pub_setup_consultation_call(dest_url, "");
}
break;
case TRANSFER_OTHER_LINE:
active_line = phone->get_active_line();
other_line = (active_line == 0 ? 1 : 0);
phone->pub_refer(active_line, other_line);
break;
}
return true;
}
bool t_userintf::exec_conference(const list<string> command_list) {
do_conference();
return true;
}
void t_userintf::do_conference(void) {
if (phone->join_3way(0, 1)) {
cout << endl;
cout << "Started 3-way conference.\n";
cout << endl;
} else {
cout << endl;
cout << "Failed to start 3-way conference.\n";
cout << endl;
}
}
bool t_userintf::exec_mute(const list<string> command_list) {
list<t_command_arg> al;
bool show_status = false;
bool toggle = true;
bool enable = true;
if (!parse_args(command_list, al)) {
exec_command("help mute");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 's':
show_status = true;
break;
case 'a':
if (i->value == "on") {
enable = true;
} else if (i->value == "off") {
enable = false;
} else {
exec_command("help mute");
return false;
}
toggle = false;
break;
default:
exec_command("help mute");
return false;
break;
}
}
do_mute(show_status, toggle, enable);
return true;
}
void t_userintf::do_mute(bool show_status, bool toggle, bool enable) {
if (show_status) {
cout << endl;
cout << "Line is ";
if (phone->is_line_muted(phone->get_active_line())) {
cout << "muted.";
} else {
cout << "not muted.";
}
cout << endl;
return;
}
if (toggle) enable = !phone->is_line_muted(phone->get_active_line());
if (enable) {
phone->mute(enable);
cout << "Line muted.\n\n";
return;
} else {
phone->mute(enable);
cout << "Line unmuted.\n\n";
return;
}
}
bool t_userintf::exec_dtmf(const list<string> command_list) {
list<t_command_arg> al;
string digits;
bool raw_mode = false;
if (phone->get_line_state(phone->get_active_line()) == LS_IDLE) {
return false;
}
if (!parse_args(command_list, al)) {
exec_command("help dtmf");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 'r':
raw_mode = true;
if (!i->value.empty()) digits = i->value;
break;
case 0:
digits = i->value;
break;
default:
exec_command("help dtmf");
return false;
break;
}
}
if (!raw_mode) {
digits = str2dtmf(digits);
}
if (digits == "") {
exec_command("help dtmf");
return false;
}
do_dtmf(digits);
return true;
}
void t_userintf::do_dtmf(const string &digits) {
const t_call_info call_info = phone->get_call_info(phone->get_active_line());
throttle_dtmf_not_supported = false;
if (!call_info.dtmf_supported) return;
for (string::const_iterator i = digits.begin(); i != digits.end(); i++) {
if (VALID_DTMF_SYM(*i)) {
phone->pub_send_dtmf(*i, call_info.dtmf_inband, call_info.dtmf_info);
}
}
}
bool t_userintf::exec_register(const list<string> command_list) {
list<t_command_arg> al;
bool reg_all_profiles = false;
if (!parse_args(command_list, al)) {
exec_command("help register");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 'a':
reg_all_profiles = true;
break;
default:
exec_command("help register");
return false;
break;
}
}
do_register(reg_all_profiles);
return true;
}
void t_userintf::do_register(bool reg_all_profiles) {
if (reg_all_profiles) {
list<t_user *> user_list = phone->ref_users();
for (list<t_user *>::iterator i = user_list.begin();
i != user_list.end(); i++)
{
phone->pub_registration(*i, REG_REGISTER,
DUR_REGISTRATION(*i));
}
} else {
phone->pub_registration(active_user, REG_REGISTER,
DUR_REGISTRATION(active_user));
}
}
bool t_userintf::exec_deregister(const list<string> command_list) {
list<t_command_arg> al;
bool dereg_all_devices = false;
bool dereg_all_profiles = false;
if (!parse_args(command_list, al)) {
exec_command("help deregister");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 'a':
dereg_all_profiles = true;
break;
case 'd':
dereg_all_devices = true;
break;
default:
exec_command("help deregister");
return false;
break;
}
}
do_deregister(dereg_all_profiles, dereg_all_devices);
return true;
}
void t_userintf::do_deregister(bool dereg_all_profiles, bool dereg_all_devices) {
t_register_type dereg_type = REG_DEREGISTER;
if (dereg_all_devices) {
dereg_type = REG_DEREGISTER_ALL;
}
if (dereg_all_profiles) {
list<t_user *> user_list = phone->ref_users();
for (list<t_user *>::iterator i = user_list.begin();
i != user_list.end(); i++)
{
phone->pub_registration(*i, dereg_type);
}
} else {
phone->pub_registration(active_user, dereg_type);
}
}
bool t_userintf::exec_fetch_registrations(const list<string> command_list) {
do_fetch_registrations();
return true;
}
void t_userintf::do_fetch_registrations(void) {
phone->pub_registration(active_user, REG_QUERY);
}
bool t_userintf::exec_options(const list<string> command_list, bool immediate) {
list<t_command_arg> al;
string destination;
bool dest_set = false;
if (!parse_args(command_list, al)) {
exec_command("help options");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 0:
destination = i->value;
dest_set = true;
break;
default:
exec_command("help options");
return false;
break;
}
}
if (!dest_set) {
if (phone->get_line_state(phone->get_active_line()) == LS_IDLE) {
exec_command("help options");
return false;
}
}
return do_options(dest_set, destination, immediate);
}
bool t_userintf::do_options(bool dest_set, const string &destination, bool immediate) {
if (!dest_set) {
phone->pub_options();
} else {
t_url dest_url;
dest_url.set_url(expand_destination(active_user, destination));
if (!dest_url.is_valid()) {
exec_command("help options");
return false;
}
phone->pub_options(active_user, dest_url);
}
return true;
}
bool t_userintf::exec_line(const list<string> command_list) {
list<t_command_arg> al;
int line = 0;
if (!parse_args(command_list, al)) {
exec_command("help line");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 0:
line = atoi(i->value.c_str());
break;
default:
exec_command("help line");
return false;
break;
}
}
if (line < 0 || line > 2) {
exec_command("help line");
return false;
}
do_line(line);
return true;
}
void t_userintf::do_line(int line) {
if (line == 0) {
cout << endl;
cout << "Active line is: " << phone->get_active_line()+1 << endl;
cout << endl;
return;
}
int current = phone->get_active_line();
if (line == current + 1) {
cout << endl;
cout << "Line " << current + 1 << " is already active.\n";
cout << endl;
return;
}
phone->pub_activate_line(line - 1);
if (phone->get_active_line() == current) {
cout << endl;
cout << "Current call cannot be put on-hold.\n";
cout << "Cannot switch to another line now.\n";
cout << endl;
} else {
cout << endl;
cout << "Line " << phone->get_active_line()+1 << " is now active.\n";
cout << endl;
}
}
bool t_userintf::exec_user(const list<string> command_list) {
list<t_command_arg> al;
string profile_name;
if (!parse_args(command_list, al)) {
exec_command("help user");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 0:
profile_name = i->value;
break;
default:
exec_command("help user");
return false;
break;
}
}
do_user(profile_name);
return true;
}
void t_userintf::do_user(const string &profile_name) {
list<t_user *> user_list = phone->ref_users();
if (profile_name.empty()) {
// Show all users
cout << endl;
for (list<t_user *>::iterator i = user_list.begin();
i != user_list.end(); i++)
{
if (*i == active_user) {
cout << "* ";
} else {
cout << " ";
}
cout << (*i)->get_profile_name();
cout << "\n ";
cout << (*i)->get_display(false);
cout << " <sip:" << (*i)->get_name();
cout << "@" << (*i)->get_domain() << ">\n";
}
cout << endl;
return;
}
for (list<t_user *>::iterator i = user_list.begin();
i != user_list.end(); i++)
{
if ((*i)->get_profile_name() == profile_name) {
active_user = (*i);
cout << endl;
cout << profile_name;
cout << " activated.\n";
cout << endl;
return;
}
}
cout << endl;
cout << "Unknown user profile: ";
cout << profile_name;
cout << endl << endl;
}
bool t_userintf::exec_zrtp(const list<string> command_list) {
list<t_command_arg> al;
t_zrtp_cmd zrtp_cmd;
if (!parse_args(command_list, al)) {
exec_command("help zrtp");
return false;
}
if (al.size() != 1) {
exec_command("help zrtp");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 0:
if (i->value == "encrypt") {
zrtp_cmd = ZRTP_ENCRYPT;
} else if (i->value == "go-clear") {
zrtp_cmd = ZRTP_GO_CLEAR;
} else if (i->value == "confirm-sas") {
zrtp_cmd = ZRTP_CONFIRM_SAS;
} else if (i->value == "reset-sas") {
zrtp_cmd = ZRTP_RESET_SAS;
} else {
exec_command("help zrtp");
return false;
}
break;
default:
exec_command("help zrtp");
return false;
break;
}
}
do_zrtp(zrtp_cmd);
return true;
}
void t_userintf::do_zrtp(t_zrtp_cmd zrtp_cmd) {
switch (zrtp_cmd) {
case ZRTP_ENCRYPT:
phone->pub_enable_zrtp();
break;
case ZRTP_GO_CLEAR:
phone->pub_zrtp_request_go_clear();
break;
case ZRTP_CONFIRM_SAS:
phone->pub_confirm_zrtp_sas();
break;
case ZRTP_RESET_SAS:
phone->pub_reset_zrtp_sas_confirmation();
break;
default:
assert(false);
}
}
bool t_userintf::exec_message(const list<string> command_list) {
list<t_command_arg> al;
string display;
string destination;
string text;
if (!parse_args(command_list, al)) {
exec_command("help message");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 'd':
display = i->value;
break;
case 0:
if (destination.empty()) {
destination = i->value;
} else {
text = i->value;
}
break;
default:
exec_command("help message");
return false;
break;
}
}
if (destination.empty() || text.empty()) {
exec_command("help message");
return false;
}
return do_message(destination, display, text);
}
bool t_userintf::do_message(const string &destination, const string &display,
const string &text)
{
t_url dest_url(expand_destination(active_user, destination));
if (!dest_url.is_valid()) {
exec_command("help message");
return false;
}
phone->pub_send_message(active_user, dest_url, display, text);
return true;
}
bool t_userintf::exec_presence(const list<string> command_list) {
list<t_command_arg> al;
t_presence_state::t_basic_state basic_state;
if (!parse_args(command_list, al)) {
exec_command("help presence");
return false;
}
for (list<t_command_arg>::iterator i = al.begin(); i != al.end(); i++) {
switch (i->flag) {
case 'b':
if (i->value == "online") {
basic_state = t_presence_state::ST_BASIC_OPEN;
} else if (i->value == "offline") {
basic_state = t_presence_state::ST_BASIC_CLOSED;
} else {
exec_command("help presence");
return false;
}
break;
default:
exec_command("help presence");
return false;
break;
}
}
do_presence(basic_state);
}
void t_userintf::do_presence(t_presence_state::t_basic_state basic_state)
{
phone->pub_publish_presence(active_user, basic_state);
}
bool t_userintf::exec_quit(const list<string> command_list) {
do_quit();
return true;
}
void t_userintf::do_quit(void) {
end_interface = true;
}
bool t_userintf::exec_help(const list<string> command_list) {
list<t_command_arg> al;
if (!parse_args(command_list, al)) {
exec_command("help help");
return false;
}
if (al.size() > 1) {
exec_command("help help");
return false;
}
do_help(al);
return true;
}
void t_userintf::do_help(const list<t_command_arg> &al) {
if (al.size() == 0) {
cout << endl;
cout << "call Call someone\n";
cout << "answer Answer an incoming call\n";
cout << "answerbye Answer an incoming call or end a call\n";
cout << "reject Reject an incoming call\n";
cout << "redirect Redirect an incoming call\n";
cout << "transfer Transfer a standing call\n";
cout << "bye End a call\n";
cout << "hold Put a call on-hold\n";
cout << "retrieve Retrieve a held call\n";
cout << "conference Join 2 calls in a 3-way conference\n";
cout << "mute Mute a line\n";
cout << "dtmf Send DTMF\n";
cout << "redial Repeat last call\n";
cout << "register Register your phone at a registrar\n";
cout << "deregister De-register your phone at a registrar\n";
cout << "fetch_reg Fetch registrations from registrar\n";
cout << "options\t\tGet capabilities of another SIP endpoint\n";
cout << "line Toggle between phone lines\n";
cout << "dnd Do not disturb\n";
cout << "auto_answer Auto answer\n";
cout << "user Show users / set active user\n";
#ifdef HAVE_ZRTP
cout << "zrtp ZRTP command for voice encryption\n";
#endif
cout << "message\t\tSend an instant message\n";
cout << "presence Publish your presence state\n";
cout << "quit Quit\n";
cout << "help Get help on a command\n";
cout << endl;
return;
}
bool ambiguous;
string c = complete_command(tolower(al.front().value), ambiguous);
if (c == "call") {
cout << endl;
cout << "Usage:\n";
cout << "\tcall [-s subject] [-d display] [-h] dst\n";
cout << "Description:\n";
cout << "\tCall someone.\n";
cout << "Arguments:\n";
cout << "\t-s subject Add a subject header to the INVITE\n";
cout << "\t-d display Add display name to To-header\n";
cout << "\t-h Hide your identity\n";
cout << "\tdst SIP uri of party to invite\n";
cout << endl;
return;
}
if (c == "answer") {
cout << endl;
cout << "Usage:\n";
cout << "\tanswer\n";
cout << "Description:\n";
cout << "\tAnswer an incoming call.\n";
cout << endl;
return;
}
if (c == "answerbye") {
cout << endl;
cout << "Usage:\n";
cout << "\tanswerbye\n";
cout << "Description:\n";
cout << "\tWith this command you can answer an incoming call or\n";
cout << "\tend an established call.\n";
cout << endl;
return;
}
if (c == "reject") {
cout << endl;
cout << "Usage:\n";
cout << "\treject\n";
cout << "Description:\n";
cout << "\tReject an incoming call. A 603 Decline response\n";
cout << "\twill be sent.\n";
cout << endl;
return;
}
if (c == "redirect") {
cout << endl;
cout << "Usage:\n";
cout << "\tredirect [-s] [-t type] [-a on|off] [dst ... dst]\n";
cout << "Description:\n";
cout << "\tRedirect an incoming call. A 302 Moved Temporarily\n";
cout << "\tresponse will be sent.\n";
cout << "\tYou can redirect the current incoming call by specifying\n";
cout << "\tone or more destinations without any other arguments.\n";
cout << "Arguments:\n";
cout << "\t-s Show which redirections are active.\n";
cout << "\t-t type\t Type for permanent redirection of calls.\n";
cout << "\t Values: always, busy, noanswer.\n";
cout << "\t-a on|off Enable/disable permanent redirection.\n";
cout << "\t The default action is 'on'.\n";
cout << "\t You can disable all redirections with the\n";
cout << "\t 'off' action and no type.\n";
cout << "\tdst SIP uri where the call should be redirected.\n";
cout << "\t You can specify up to 5 destinations.\n";
cout << "\t The destinations will be tried in sequence.\n";
cout << "Examples:\n";
cout << "\tRedirect current incoming call to michel@twinklephone.com\n";
cout << "\tredirect michel@twinklephone.com\n";
cout << endl;
cout << "\tRedirect busy calls permanently to michel@twinklephone.com\n";
cout << "\tredirect -t busy michel@twinklephone.com\n";
cout << endl;
cout << "\tDisable redirection of busy calls.\n";
cout << "\tredirect -t busy -a off\n";
cout << endl;
return;
}
if (c == "transfer") {
cout << endl;
cout << "Usage:\n";
cout << "\ttransfer [-c] [-l] [dst]\n";
cout << "Description:\n";
cout << "\tTransfer a standing call to another destination.\n";
cout << "\tFor a transfer with consultation, first use the -c flag with a\n";
cout << "\tdestination. This sets up the consultation call. When the\n";
cout << "\tconsulted party agrees, give the command with the -c flag once\n";
cout << "\tmore, but now without a destination. This transfers the call.\n";
cout << "Arguments:\n";
cout << "\t-c Consult destination before transferring call.\n";
cout << "\t-l Transfer call to party on other line.\n";
cout << "\tdst SIP uri of transfer destination\n";
cout << endl;
return;
}
if (c == "bye") {
cout << endl;
cout << "Usage:\n";
cout << "\tbye\n";
cout << "Description:\n";
cout << "\tEnd a call.\n";
cout << "\tFor a stable call a BYE will be sent.\n";
cout << "\tIf the invited party did not yet sent a final answer,\n";
cout << "\tthen a CANCEL will be sent.\n";
cout << endl;
return;
}
if (c == "hold") {
cout << endl;
cout << "Usage:\n";
cout << "\thold\n";
cout << "Description:\n";
cout << "\tPut the current call on the acitve line on-hold.\n";
cout << endl;
return;
}
if (c == "retrieve") {
cout << endl;
cout << "Usage:\n";
cout << "\tretrieve\n";
cout << "Description:\n";
cout << "\tRetrieve a held call on the active line.\n";
cout << endl;
return;
}
if (c == "conference") {
cout << endl;
cout << "Usage:\n";
cout << "\tconference\n";
cout << "Description:\n";
cout << "\tJoin 2 calls in a 3-way conference. Before you give this\n";
cout << "\tcommand you must have a call on each line.\n";
cout << endl;
return;
}
if (c == "mute") {
cout << endl;
cout << "Usage:\n";
cout << "\tmute [-s] [-a on|off]\n";
cout << "Description:\n";
cout << "\tMute/unmute the active line.\n";
cout << "\tYou can hear the other side of the line, but they cannot\n";
cout << "\thear you.\n";
cout << "Arguments:\n";
cout << "\t-s Show if line is muted.\n";
cout << "\t-a on|off Mute/unmute.\n";
cout << "Notes:\n";
cout << "\tWithout any arguments you can toggle the status.\n";
cout << endl;
return;
}
if (c == "dtmf") {
cout << endl;
cout << "Usage:\n";
cout << "\tdtmf digits\n";
cout << "Description:\n";
cout << "\tSend the digits as out-of-band DTMF telephone events ";
cout << "(RFC 2833).\n";
cout << "\tThis command can only be given when a call is ";
cout << "established.\n";
cout << "Arguments:\n";
cout << "\t-r Raw mode: do not convert letters to digits.\n";
cout << "\tdigits 0-9 | A-D | * | #\n";
cout << "Example:\n";
cout << "\tdtmf 1234#\n";
cout << "\tdmtf movies\n";
cout << "Notes:\n";
cout << "\tThe overdecadic digits A-D can only be sent in raw mode.\n";
cout << endl;
return;
}
if (c == "redial") {
cout << endl;
cout << "Usage:\n";
cout << "\tredial\n";
cout << "Description:\n";
cout << "\tRepeat last call.\n";
cout << endl;
return;
}
if (c == "register") {
cout << endl;
cout << "Usage:\n";
cout << "\tregister\n";
cout << "Description:\n";
cout << "\tRegister your phone at a registrar.\n";
cout << "Arguments:\n";
cout << "\t-a Register all enabled user profiles.\n";
cout << endl;
return;
}
if (c == "deregister") {
cout << endl;
cout << "Usage:\n";
cout << "\tderegister [-a]\n";
cout << "Description:\n";
cout << "\tDe-register your phone at a registrar.\n";
cout << "Arguments:\n";
cout << "\t-a De-register all enabled user profiles.\n";
cout << "\t-d De-register all devices.\n";
cout << endl;
return;
}
if (c == "fetch_reg") {
cout << endl;
cout << "Usage:\n";
cout << "\tfetch_reg\n";
cout << "Description:\n";
cout << "\tFetch current registrations from registrar.\n";
cout << endl;
return;
}
if (c == "options") {
cout << endl;
cout << "Usage:\n";
cout << "\toptions [dst]\n";
cout << "Description:\n";
cout << "\tGet capabilities of another SIP endpoint.\n";
cout << "\tIf no destination is passed as an argument, then\n";
cout << "\tthe capabilities of the far-end in the current call\n";
cout << "\ton the active line are requested.\n";
cout << "Arguments:\n";
cout << "\tdst SIP uri of end-point\n";
cout << endl;
return;
}
if (c == "line") {
cout << endl;
cout << "Usage:\n";
cout << "\tline [lineno]\n";
cout << "Description:\n";
cout << "\tIf no argument is passed then the current active ";
cout << "line is shown\n";
cout << "\tOtherwise switch to another line. If the current active\n";
cout << "\thas a call, then this call will be put on-hold.\n";
cout << "\tIf the new active line has a held call, then this call\n";
cout << "\twill be retrieved.\n";
cout << "Arguments:\n";
cout << "\tlineno Switch to another line (values = ";
cout << "1,2)\n";
cout << endl;
return;
}
if (c == "dnd") {
cout << endl;
cout << "Usage:\n";
cout << "\tdnd [-s] [-a on|off]\n";
cout << "Description:\n";
cout << "\tEnable/disable the do not disturb service.\n";
cout << "\tIf dnd is enabled then a 480 Temporarily Unavailable ";
cout << "response is given\n";
cout << "\ton incoming calls.\n";
cout << "Arguments:\n";
cout << "\t-s Show if dnd is active.\n";
cout << "\t-a on|off Enable/disable dnd.\n";
cout << "Notes:\n";
cout << "\tWithout any arguments you can toggle the status.\n";
cout << endl;
return;
}
if (c == "auto_answer") {
cout << endl;
cout << "Usage:\n";
cout << "\tauto_answer [-s] [-a on|off]\n";
cout << "Description:\n";
cout << "\tEnable/disable the auto answer service.\n";
cout << "Arguments:\n";
cout << "\t-s Show if auto answer is active.\n";
cout << "\t-a on|off Enable/disable auto answer.\n";
cout << "Notes:\n";
cout << "\tWithout any arguments you can toggle the status.\n";
cout << endl;
return;
}
if (c == "user") {
cout << endl;
cout << "Usage:\n";
cout << "\tuser [profile name]\n";
cout << "Description:\n";
cout << "\tMake a user profile the active profile.\n";
cout << "\tCommands like 'invite' are executed for the active profile.\n";
cout << "\tWithout an argument this command lists all users. The active\n";
cout << "\tuser will be marked with '*'.\n";
cout << "Arguments:\n";
cout << "\tprofile name The user profile to activate.\n";
cout << endl;
return;
}
#ifdef HAVE_ZRTP
if (c == "zrtp") {
cout << endl;
cout << "Usage:\n";
cout << "\tzrtp <zrtp-command>\n";
cout << "Description:\n";
cout << "\tExecute a ZRTP command.\n";
cout << "ZRTP commands:\n";
cout << "\tencrypt Start ZRTP negotiation for encryption.\n";
cout << "\tgo-clear Send ZRTP go-clear request.\n";
cout << "\tconfirm-sas Confirm the SAS value.\n";
cout << "\treset-sas Reset SAS confirmation.\n";
cout << endl;
return;
}
#endif
if (c == "message") {
cout << endl;
cout << "Usage:\n";
cout << "\tmessage [-d display] dst text\n";
cout << "Description:\n";
cout << "\tSend an instant message.\n";
cout << "Arguments:\n";
cout << "\t-d display Add display name to To-header\n";
cout << "\tdst SIP uri of party to message\n";
cout << "\ttext Message text to send. Surround with double quotes\n";
cout << "\t\t\twhen your text contains whitespace.\n";
cout << endl;
return;
}
if (c == "presence") {
cout << endl;
cout << "Usage:\n";
cout << "\tpresence -b [online|offline]\n";
cout << "Description:\n";
cout << "\tPublish your presence state to a presence agent\n";
cout << "Arguments:\n";
cout << "\t-b A basic presence state: online or offline\n";
cout << endl;
return;
}
if (c == "quit") {
cout << endl;
cout << "Usage:\n";
cout << "\tquit\n";
cout << "Description:\n";
cout << "\tQuit.\n";
cout << endl;
return;
}
if (c == "help") {
cout << endl;
cout << "Usage:\n";
cout << "\thelp [command]\n";
cout << "Description:\n";
cout << "\tShow help on a command.\n";
cout << "Arguments:\n";
cout << "\tcommand Command you want help with\n";
cout << endl;
return;
}
cout << endl;
cout << "\nUnknown command\n\n";
cout << endl;
}
/////////////////////////////
// Public
/////////////////////////////
t_userintf::t_userintf(t_phone *_phone) {
phone = _phone;
end_interface = false;
tone_gen = NULL;
active_user = NULL;
use_stdout = true;
throttle_dtmf_not_supported = false;
thr_process_events = NULL;
all_commands.push_back("invite");
all_commands.push_back("call");
all_commands.push_back("answer");
all_commands.push_back("answerbye");
all_commands.push_back("reject");
all_commands.push_back("redirect");
all_commands.push_back("bye");
all_commands.push_back("hold");
all_commands.push_back("retrieve");
all_commands.push_back("refer");
all_commands.push_back("transfer");
all_commands.push_back("conference");
all_commands.push_back("mute");
all_commands.push_back("dtmf");
all_commands.push_back("redial");
all_commands.push_back("register");
all_commands.push_back("deregister");
all_commands.push_back("fetch_reg");
all_commands.push_back("options");
all_commands.push_back("line");
all_commands.push_back("dnd");
all_commands.push_back("auto_answer");
all_commands.push_back("user");
#ifdef HAVE_ZRTP
all_commands.push_back("zrtp");
#endif
all_commands.push_back("message");
all_commands.push_back("presence");
all_commands.push_back("quit");
all_commands.push_back("exit");
all_commands.push_back("q");
all_commands.push_back("x");
all_commands.push_back("help");
all_commands.push_back("h");
all_commands.push_back("?");
}
t_userintf::~t_userintf() {
if (tone_gen) {
MEMMAN_DELETE(tone_gen);
delete tone_gen;
}
if (thr_process_events) {
evq_ui_events.push_quit();
thr_process_events->join();
log_file->write_report("thr_process_events stopped.",
"t_userintf::~t_userintf", LOG_NORMAL, LOG_DEBUG);
MEMMAN_DELETE(thr_process_events);
delete thr_process_events;
}
}
string t_userintf::complete_command(const string &c, bool &ambiguous) {
ambiguous = false;
string full_command;
for (list<string>::const_iterator i = all_commands.begin();
i != all_commands.end(); i++)
{
// If there is an exact match, then this is the command.
// This allows a one command to be a prefix of another command.
if (c == *i) {
ambiguous = false;
return c;
}
if (c.size() < i->size() && c == i->substr(0, c.size())) {
if (full_command != "") {
ambiguous = true;
// Do not return here, as there might still be
// an exact match.
}
full_command = *i;
}
}
if (ambiguous) return "";
return full_command;
}
bool t_userintf::exec_command(const string &command_line, bool immediate) {
vector<string> v = split_ws(command_line, true);
if (v.size() == 0) return false;
bool ambiguous;
string command = complete_command(tolower(v[0]), ambiguous);
if (ambiguous) {
if (use_stdout) {
cout << endl;
cout << "Ambiguous command\n";
cout << endl;
}
return false;
}
list<string> l(v.begin(), v.end());
if (command == "invite") return exec_invite(l, immediate);
if (command == "call") return exec_invite(l, immediate);
if (command == "answer") return exec_answer(l);
if (command == "answerbye") return exec_answerbye(l);
if (command == "reject") return exec_reject(l);
if (command == "redirect") return exec_redirect(l, immediate);
if (command == "bye") return exec_bye(l);
if (command == "hold") return exec_hold(l);
if (command == "retrieve") return exec_retrieve(l);
if (command == "refer") return exec_refer(l, immediate);
if (command == "transfer") return exec_refer(l, immediate);
if (command == "conference") return exec_conference(l);
if (command == "mute") return exec_mute(l);
if (command == "dtmf") return exec_dtmf(l);
if (command == "redial") return exec_redial(l);
if (command == "register") return exec_register(l);
if (command == "deregister") return exec_deregister(l);
if (command == "fetch_reg") return exec_fetch_registrations(l);
if (command == "options") return exec_options(l, immediate);
if (command == "line") return exec_line(l);
if (command == "dnd") return exec_dnd(l);
if (command == "auto_answer") return exec_auto_answer(l);
if (command == "user") return exec_user(l);
#ifdef HAVE_ZRTP
if (command == "zrtp") return exec_zrtp(l);
#endif
if (command == "message") return exec_message(l);
if (command == "presence") return exec_presence(l);
if (command == "quit") return exec_quit(l);
if (command == "exit") return exec_quit(l);
if (command == "x") return exec_quit(l);
if (command == "q") return exec_quit(l);
if (command == "help") return exec_help(l);
if (command == "h") return exec_help(l);
if (command == "?") return exec_help(l);
if (use_stdout) {
cout << endl;
cout << "Unknown command\n";
cout << endl;
}
return false;
}
string t_userintf::format_sip_address(t_user *user_config, const string &display,
const t_url &uri) const
{
string s;
if (uri.encode() == ANONYMOUS_URI) {
return TRANSLATE("Anonymous");
}
s = display;
if (display != "") s += " <";
if (user_config->get_display_useronly_phone() &&
uri.is_phone(user_config->get_numerical_user_is_phone(),
user_config->get_special_phone_symbols()))
{
// Display telephone number only
s += user_config->convert_number(uri.get_user());
} else {
// Display full URI
// Convert the username according to the number conversion
// rules.
t_url u(uri);
string username = user_config->convert_number(u.get_user());
if (username != u.get_user()) {
u.set_user(username);
}
s += u.encode_no_params_hdrs(false);
}
if (display != "") s += ">";
return s;
}
list<string> t_userintf::format_warnings(const t_hdr_warning &hdr_warning) const {
string s;
list<string> l;
for (list<t_warning>::const_iterator i = hdr_warning.warnings.begin();
i != hdr_warning.warnings.end(); i++)
{
s = TRANSLATE("Warning:");
s += " ";
s += int2str(i->code);
s += ' ';
s += i->text;
s += " (";
s += i->host;
if (i->port > 0) s += int2str(i->port, ":%d");
s += ')';
l.push_back(s);
}
return l;
}
string t_userintf::format_codec(t_audio_codec codec) const {
switch (codec) {
case CODEC_NULL: return "null";
case CODEC_UNSUPPORTED: return "???";
case CODEC_G711_ALAW: return "g711a";
case CODEC_G711_ULAW: return "g711u";
case CODEC_GSM: return "gsm";
case CODEC_SPEEX_NB: return "spx-nb";
case CODEC_SPEEX_WB: return "spx-wb";
case CODEC_SPEEX_UWB: return "spx-uwb";
case CODEC_ILBC: return "ilbc";
case CODEC_G726_16: return "g726-16";
case CODEC_G726_24: return "g726-24";
case CODEC_G726_32: return "g726-32";
case CODEC_G726_40: return "g726-40";
default: return "???";
}
}
void t_userintf::run(void) {
string command_line;
// Start asynchronous event processor
thr_process_events = new t_thread(process_events_main, NULL);
MEMMAN_NEW(thr_process_events);
list<t_user *> user_list = phone->ref_users();
active_user = user_list.front();
cout << PRODUCT_NAME << " " << PRODUCT_VERSION << ", " << PRODUCT_DATE;
cout << endl;
cout << "Copyright (C) 2005-2007 " << PRODUCT_AUTHOR << endl;
cout << endl;
cout << "Users:";
exec_command("user");
cout << "Local IP: " << user_host << endl;
cout << endl;
restore_state();
// Initialize phone functions
phone->init();
while (!end_interface) {
cout << CLI_PROMPT;
getline(cin, command_line);
exec_command(command_line);
if (cin.eof()) {
cout << endl;
break;
}
}
// Terminate phone functions
phone->terminate();
save_state();
cout << endl;
}
void t_userintf::process_events(void) {
t_event *event;
t_event_ui *ui_event;
bool quit = false;
while (!quit) {
event = evq_ui_events.pop();
switch (event->get_type()) {
case EV_UI:
ui_event = dynamic_cast<t_event_ui *>(event);
assert(ui_event);
ui_event->exec(this);
break;
case EV_QUIT:
quit = true;
break;
default:
assert(false);
break;
}
MEMMAN_DELETE(event);
delete event;
}
}
void t_userintf::save_state(void) {
string err_msg;
sys_config->set_redial_url(last_called_url);
sys_config->set_redial_display(last_called_display);
sys_config->set_redial_subject(last_called_subject);
sys_config->set_redial_profile(last_called_profile);
sys_config->set_redial_hide_user(last_called_hide_user);
sys_config->write_config(err_msg);
}
void t_userintf::restore_state(void) {
last_called_url = sys_config->get_redial_url();
last_called_display = sys_config->get_redial_display();
last_called_subject = sys_config->get_redial_subject();
last_called_profile = sys_config->get_redial_profile();
last_called_hide_user = sys_config->get_redial_hide_user();
}
void t_userintf::lock(void) {
assert(!is_prohibited_thread());
// TODO: lock for CLI
}
void t_userintf::unlock(void) {
// TODO: lock for CLI
}
string t_userintf::select_network_intf(void) {
string ip;
list<t_interface> *l = get_interfaces();
// As memman has no hooks in the socket routines, report it here.
MEMMAN_NEW(l);
if (l->size() == 0) {
// cout << "Cannot find a network interface\n";
cout << "Cannot find a network interface. Twinkle will use\n"
"127.0.0.1 as the local IP address. When you connect to\n"
"the network you have to restart Twinkle to use the correct\n"
"IP address.\n";
MEMMAN_DELETE(l);
delete l;
return "127.0.0.1";
}
if (l->size() == 1) {
ip = l->front().get_ip_addr();
} else {
int num = 1;
cout << "Multiple network interfaces found.\n";
for (list<t_interface>::iterator i = l->begin(); i != l->end(); i++) {
cout << num << ") " << i->name << ": ";
cout << i->get_ip_addr() << endl;
num++;
}
cout << endl;
int selection = 0;
while (selection < 1 || selection > l->size()) {
cout << "Which interface do you want to use (enter number): ";
string choice;
getline(cin, choice);
selection = atoi(choice.c_str());
}
num = 1;
for (list<t_interface>::iterator i = l->begin(); i != l->end(); i++) {
if (num == selection) {
ip = i->get_ip_addr();
break;
}
num++;
}
}
MEMMAN_DELETE(l);
delete l;
return ip;
}
bool t_userintf::select_user_config(list<string> &config_files) {
// In CLI mode, simply select the default config file
config_files.clear();
config_files.push_back(USER_CONFIG_FILE);
return true;
}
void t_userintf::cb_incoming_call(t_user *user_config, int line, const t_request *r) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": ";
cout << "incoming call\n";
cout << "From:\t\t";
string from_party = format_sip_address(user_config,
r->hdr_from.get_display_presentation(), r->hdr_from.uri);
cout << from_party << endl;
if (r->hdr_organization.is_populated()) {
cout << "Organization:\t" << r->hdr_organization.name << endl;
}
cout << "To:\t\t";
cout << format_sip_address(user_config, r->hdr_to.display, r->hdr_to.uri) << endl;
if (r->hdr_referred_by.is_populated()) {
cout << "Referred-by:\t";
cout << format_sip_address(user_config, r->hdr_referred_by.display,
r->hdr_referred_by.uri);
cout << endl;
}
if (r->hdr_subject.is_populated()) {
cout << "Subject:\t" << r->hdr_subject.subject << endl;
}
cout << endl;
cout << CLI_PROMPT;
cout.flush();
cb_notify_call(line, from_party);
}
void t_userintf::cb_call_cancelled(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": ";
cout << "far end cancelled call.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
cb_stop_call_notification(line);
}
void t_userintf::cb_far_end_hung_up(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": ";
cout << "far end ended call.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
cb_stop_call_notification(line);
}
void t_userintf::cb_answer_timeout(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": ";
cout << "answer timeout.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
cb_stop_call_notification(line);
}
void t_userintf::cb_sdp_answer_not_supported(int line, const string &reason) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": ";
cout << "SDP answer from far end not supported.\n";
cout << reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
cb_stop_call_notification(line);
}
void t_userintf::cb_sdp_answer_missing(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": ";
cout << "SDP answer from far end missing.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
cb_stop_call_notification(line);
}
void t_userintf::cb_unsupported_content_type(int line, const t_sip_message *r) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": ";
cout << "Unsupported content type in answer from far end.\n";
cout << r->hdr_content_type.media.type << "/";
cout << r->hdr_content_type.media.subtype << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
cb_stop_call_notification(line);
}
void t_userintf::cb_ack_timeout(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": ";
cout << "no ACK received, call will be terminated.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
cb_stop_call_notification(line);
}
void t_userintf::cb_100rel_timeout(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": ";
cout << "no PRACK received, call will be terminated.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
cb_stop_call_notification(line);
}
void t_userintf::cb_prack_failed(int line, const t_response *r) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": PRACK failed.\n";
cout << r->code << ' ' << r->reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
cb_stop_call_notification(line);
}
void t_userintf::cb_provisional_resp_invite(int line, const t_response *r) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": received ";
cout << r->code << ' ' << r->reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_cancel_failed(int line, const t_response *r) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": cancel failed.\n";
cout << r->code << ' ' << r->reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_call_answered(t_user *user_config, int line, const t_response *r) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": far end answered call.\n";
cout << r->code << ' ' << r->reason << endl;
cout << "To: ";
cout << format_sip_address(user_config, r->hdr_to.display, r->hdr_to.uri) << endl;
if (r->hdr_organization.is_populated()) {
cout << "Organization: " << r->hdr_organization.name << endl;
}
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_call_failed(t_user *user_config, int line, const t_response *r) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": call failed.\n";
cout << r->code << ' ' << r->reason << endl;
// Warnings
if (r->hdr_warning.is_populated()) {
list<string> l = format_warnings(r->hdr_warning);
for (list<string>::iterator i = l.begin(); i != l.end(); i++) {
cout << *i << endl;
}
}
// Redirection response
if (r->get_class() == R_3XX && r->hdr_contact.is_populated()) {
list<t_contact_param> l = r->hdr_contact.contact_list;
l.sort();
cout << "You can try the following contacts:\n";
for (list<t_contact_param>::iterator i = l.begin();
i != l.end(); i++)
{
cout << format_sip_address(user_config, i->display, i->uri) << endl;
}
}
// Unsupported extensions
if (r->code == R_420_BAD_EXTENSION) {
cout << r->hdr_unsupported.encode();
}
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_stun_failed_call_ended(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": call failed.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_call_ended(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": call ended.\n";
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_call_established(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": call established.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_options_response(const t_response *r) {
cout << endl;
cout << "OPTIONS response received: ";
cout << r->code << ' ' << r->reason << endl;
cout << "Capabilities of " << r->hdr_to.uri.encode() << endl;
cout << "Accepted body types\n";
if (r->hdr_accept.is_populated()) {
cout << "\t" << r->hdr_accept.encode();
} else {
cout << "\tUnknown\n";
}
cout << "Accepted encodings\n";
if (r->hdr_accept_encoding.is_populated()) {
cout << "\t" << r->hdr_accept_encoding.encode();
} else {
cout << "\tUnknown\n";
}
cout << "Accepted languages\n";
if (r->hdr_accept_language.is_populated()) {
cout << "\t" << r->hdr_accept_language.encode();
} else {
cout << "\tUnknown\n";
}
cout << "Allowed requests\n";
if (r->hdr_allow.is_populated()) {
cout << "\t" << r->hdr_allow.encode();
} else {
cout << "\tUnknown\n";
}
cout << "Supported extensions\n";
if (r->hdr_supported.is_populated()) {
if (r->hdr_supported.features.empty()) {
cout << "\tNone\n";
} else {
cout << "\t" << r->hdr_supported.encode();
}
} else {
cout << "\tUnknown\n";
}
cout << "End point type\n";
bool endpoint_known = false;
if (r->hdr_server.is_populated()) {
cout << "\t" << r->hdr_server.encode();
endpoint_known = true;
}
if (r->hdr_user_agent.is_populated()) {
// Some end-point put a User-Agent header in the response
// instead of a Server header.
cout << "\t" << r->hdr_user_agent.encode();
endpoint_known = true;
}
if (!endpoint_known) {
cout << "\tUnknown\n";
}
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_reinvite_success(int line, const t_response *r) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": re-INVITE successful.\n";
cout << r->code << ' ' << r->reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_reinvite_failed(int line, const t_response *r) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": re-INVITE failed.\n";
cout << r->code << ' ' << r->reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_retrieve_failed(int line, const t_response *r) {
if (line >= NUM_USER_LINES) return;
// The status code from the response has already been reported
// by cb_reinvite_failed.
cout << endl;
cout << "Line " << line + 1 << ": retrieve failed.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_invalid_reg_resp(t_user *user_config,
const t_response *r, const string &reason)
{
cout << endl;
cout << user_config->get_profile_name();
cout << ", registration failed: " << r->code << ' ' << r->reason << endl;
cout << reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_register_success(t_user *user_config,
const t_response *r, unsigned long expires, bool first_success)
{
// Only report success if this is the first success in a sequence
if (!first_success) return;
cout << endl;
cout << user_config->get_profile_name();
cout << ": registration succeeded (expires = " << expires << " seconds)\n";
// Date at registrar
if (r->hdr_date.is_populated()) {
cout << "Registrar ";
cout << r->hdr_date.encode() << endl;
}
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_register_failed(t_user *user_config,
const t_response *r, bool first_failure)
{
// Only report the first failure in a sequence of failures
if (!first_failure) return;
cout << endl;
cout << user_config->get_profile_name();
cout << ", registration failed: " << r->code << ' ' << r->reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_register_stun_failed(t_user *user_config, bool first_failure) {
// Only report the first failure in a sequence of failures
if (!first_failure) return;
cout << endl;
cout << user_config->get_profile_name();
cout << ", registration failed: STUN failure";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_deregister_success(t_user *user_config, const t_response *r) {
cout << endl;
cout << user_config->get_profile_name();
cout << ", de-registration succeeded: " << r->code << ' ' << r->reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_deregister_failed(t_user *user_config, const t_response *r) {
cout << endl;
cout << user_config->get_profile_name();
cout << ", de-registration failed: " << r->code << ' ' << r->reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_fetch_reg_failed(t_user *user_config, const t_response *r) {
cout << endl;
cout << user_config->get_profile_name();
cout << ", fetch registrations failed: " << r->code << ' ' << r->reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_fetch_reg_result(t_user *user_config, const t_response *r) {
cout << endl;
cout << user_config->get_profile_name();
const list<t_contact_param> &l = r->hdr_contact.contact_list;
if (l.size() == 0) {
cout << ": you are not registered\n";
} else {
cout << ": you have the following registrations\n";
for (list<t_contact_param>::const_iterator i = l.begin();
i != l.end(); i++)
{
cout << i->encode() << endl;
}
}
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_register_inprog(t_user *user_config, t_register_type register_type) {
switch (register_type) {
case REG_REGISTER:
// Do not report a register refreshment
if (phone->get_is_registered(user_config)) return;
// Do not report an automatic register re-attempt
if (phone->get_last_reg_failed(user_config)) return;
cout << endl;
cout << user_config->get_profile_name();
cout << ": registering phone...\n";
break;
case REG_DEREGISTER:
cout << endl;
cout << user_config->get_profile_name();
cout << ": deregistering phone...\n";
break;
case REG_DEREGISTER_ALL:
cout << endl;
cout << user_config->get_profile_name();
cout << ": deregistering all phones...";
break;
case REG_QUERY:
cout << endl;
cout << user_config->get_profile_name();
cout << ": fetching registrations...";
break;
default:
assert(false);
}
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_redirecting_request(t_user *user_config,
int line, const t_contact_param &contact)
{
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": redirecting request to:\n";
cout << format_sip_address(user_config, contact.display, contact.uri) << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_redirecting_request(t_user *user_config, const t_contact_param &contact) {
cout << endl;
cout << "Redirecting request to: ";
cout << format_sip_address(user_config, contact.display, contact.uri) << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_play_ringtone(int line) {
if (!sys_config->get_play_ringtone()) return;
if (tone_gen) {
tone_gen->stop();
MEMMAN_DELETE(tone_gen);
delete tone_gen;
}
// Determine ring tone
string ringtone_file = phone->get_ringtone(line);
tone_gen = new t_tone_gen(ringtone_file, sys_config->get_dev_ringtone());
MEMMAN_NEW(tone_gen);
// If ring tone does not exist, then fall back to system default.
if (!tone_gen->is_valid() && ringtone_file != FILE_RINGTONE) {
MEMMAN_DELETE(tone_gen);
delete tone_gen;
tone_gen = new t_tone_gen(FILE_RINGTONE, sys_config->get_dev_ringtone());
MEMMAN_NEW(tone_gen);
}
// Play ring tone
tone_gen->start_play_thread(true, INTERVAL_RINGTONE);
}
void t_userintf::cb_play_ringback(t_user *user_config) {
if (!sys_config->get_play_ringback()) return;
if (tone_gen) {
tone_gen->stop();
MEMMAN_DELETE(tone_gen);
delete tone_gen;
}
// Determine ring back tone
string ringback_file;
if (!user_config->get_ringback_file().empty()) {
ringback_file = user_config->get_ringback_file();
} else if (!sys_config->get_ringback_file().empty()) {
ringback_file = sys_config->get_ringback_file();
} else {
// System default
ringback_file = FILE_RINGBACK;
}
tone_gen = new t_tone_gen(ringback_file, sys_config->get_dev_speaker());
MEMMAN_NEW(tone_gen);
// If ring back tone does not exist, then fall back to system default.
if (!tone_gen->is_valid() && ringback_file != FILE_RINGBACK) {
MEMMAN_DELETE(tone_gen);
delete tone_gen;
tone_gen = new t_tone_gen(FILE_RINGBACK, sys_config->get_dev_speaker());
MEMMAN_NEW(tone_gen);
}
// Play ring back tone
tone_gen->start_play_thread(true, INTERVAL_RINGBACK);
}
void t_userintf::cb_stop_tone(int line) {
// Only stop the tone if the current line is the active line
if (line != phone->get_active_line()) return;
if (!tone_gen) return;
tone_gen->stop();
MEMMAN_DELETE(tone_gen);
delete tone_gen;
tone_gen = NULL;
}
void t_userintf::cb_notify_call(int line, string from_party) {
// Play ringtone if the call is received on the active line
if (line == phone->get_active_line() &&
!phone->is_line_auto_answered(line))
{
cb_play_ringtone(line);
}
}
void t_userintf::cb_stop_call_notification(int line) {
cb_stop_tone(line);
}
void t_userintf::cb_dtmf_detected(int line, char dtmf_event) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": DTMF detected: ";
if (VALID_DTMF_EV(dtmf_event)) {
cout << dtmf_ev2char(dtmf_event) << endl;
} else {
cout << "invalid DTMF telephone event (" << (int)dtmf_event << endl;
}
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_async_dtmf_detected(int line, char dtmf_event) {
if (line >= NUM_USER_LINES) return;
t_event_ui *event = new t_event_ui(TYPE_UI_CB_DTMF_DETECTED);
MEMMAN_NEW(event);
event->set_line(line);
event->set_dtmf_event(dtmf_event);
evq_ui_events.push(event);
}
void t_userintf::cb_send_dtmf(int line, char dtmf_event) {
// No feed back in CLI
}
void t_userintf::cb_async_send_dtmf(int line, char dtmf_event) {
t_event_ui *event = new t_event_ui(TYPE_UI_CB_SEND_DTMF);
MEMMAN_NEW(event);
event->set_line(line);
event->set_dtmf_event(dtmf_event);
evq_ui_events.push(event);
}
void t_userintf::cb_dtmf_not_supported(int line) {
if (line >= NUM_USER_LINES) return;
if (throttle_dtmf_not_supported) return;
cout << endl;
cout << "Line " << line + 1 << ": far end does not support DTMF events.\n";
cout << endl;
cout.flush();
// Throttle subsequent call backs
throttle_dtmf_not_supported = true;
}
void t_userintf::cb_dtmf_supported(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": far end supports DTMF telephone event.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_line_state_changed(void) {
// Nothing to do for CLI
}
void t_userintf::cb_async_line_state_changed(void) {
t_event_ui *event = new t_event_ui(TYPE_UI_CB_LINE_STATE_CHANGED);
MEMMAN_NEW(event);
evq_ui_events.push(event);
}
void t_userintf::cb_send_codec_changed(int line, t_audio_codec codec) {
// No feedback in CLI
}
void t_userintf::cb_recv_codec_changed(int line, t_audio_codec codec) {
// No feedback in CLI
}
void t_userintf::cb_async_recv_codec_changed(int line, t_audio_codec codec) {
t_event_ui *event = new t_event_ui(TYPE_UI_CB_RECV_CODEC_CHANGED);
MEMMAN_NEW(event);
event->set_line(line);
event->set_codec(codec);
evq_ui_events.push(event);
}
void t_userintf::cb_notify_recvd(int line, const t_request *r) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": received notification.\n";
cout << "Event: " << r->hdr_event.event_type << endl;
cout << "State: " << r->hdr_subscription_state.substate << endl;
if (r->hdr_subscription_state.substate == SUBSTATE_TERMINATED) {
cout << "Reason: " << r->hdr_subscription_state.reason << endl;
}
t_response *sipfrag = (t_response *)((t_sip_body_sipfrag *)r->body)->sipfrag;
cout << "Progress: " << sipfrag->code << ' ' << sipfrag->reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_refer_failed(int line, const t_response *r) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": refer request failed.\n";
cout << r->code << ' ' << r->reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_refer_result_success(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": call succesfully referred.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_refer_result_failed(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": call refer failed.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_refer_result_inprog(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": call refer in progress.\n";
cout << "No further notifications will be received.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_call_referred(t_user *user_config, int line, t_request *r) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": transferring call to ";
cout << format_sip_address(user_config, r->hdr_refer_to.display,
r->hdr_refer_to.uri);
cout << endl;
if (r->hdr_referred_by.is_populated()) {
cout << "Tranfer requested by ";
cout << format_sip_address(user_config, r->hdr_referred_by.display,
r->hdr_referred_by.uri);
cout << endl;
}
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_retrieve_referrer(t_user *user_config, int line) {
if (line >= NUM_USER_LINES) return;
const t_call_info call_info = phone->get_call_info(line);
cout << endl;
cout << "Line " << line + 1 << ": call transfer failed.\n";
cout << "Retrieving call: \n";
cout << "From: ";
cout << format_sip_address(user_config, call_info.from_display, call_info.from_uri);
cout << endl;
if (!call_info.from_organization.empty()) {
cout << " " << call_info.from_organization;
cout << endl;
}
cout << "To: ";
cout << format_sip_address(user_config, call_info.to_display, call_info.to_uri);
cout << endl;
if (!call_info.to_organization.empty()) {
cout << " " << call_info.to_organization;
cout << endl;
}
cout << "Subject: ";
cout << call_info.subject;
cout << endl << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_consultation_call_setup(t_user *user_config, int line) {
if (line >= NUM_USER_LINES) return;
const t_call_info call_info = phone->get_call_info(line);
cout << endl;
cout << "Line " << line + 1 << ": setup consultation call.\n";
cout << "From: ";
cout << format_sip_address(user_config, call_info.from_display, call_info.from_uri);
cout << endl;
if (!call_info.from_organization.empty()) {
cout << " " << call_info.from_organization;
cout << endl;
}
cout << "To: ";
cout << format_sip_address(user_config, call_info.to_display, call_info.to_uri);
cout << endl;
if (!call_info.to_organization.empty()) {
cout << " " << call_info.to_organization;
cout << endl;
}
cout << "Subject: ";
cout << call_info.subject;
cout << endl << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_stun_failed(t_user *user_config, int err_code, const string &err_reason) {
cout << endl;
cout << user_config->get_profile_name();
cout << ", STUN request failed: ";
cout << err_code << " " << err_reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_stun_failed(t_user *user_config) {
cout << endl;
cout << user_config->get_profile_name();
cout << ", STUN request failed.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
bool t_userintf::cb_ask_user_to_redirect_invite(t_user *user_config,
const t_url &destination, const string &display)
{
// Cannot ask user for permission in CLI, so deny redirection.
return false;
}
bool t_userintf::cb_ask_user_to_redirect_request(t_user *user_config,
const t_url &destination, const string &display, t_method method)
{
// Cannot ask user for permission in CLI, so deny redirection.
return false;
}
bool t_userintf::cb_ask_credentials(t_user *user_config,
const string &realm, string &username, string &password)
{
// Cannot ask user for username/password in CLI
return false;
}
void t_userintf::cb_ask_user_to_refer(t_user *user_config,
const t_url &refer_to_uri,
const string &refer_to_display,
const t_url &referred_by_uri,
const string &referred_by_display)
{
// Cannot ask user for permission in CLI, so deny REFER
send_refer_permission(false);
}
void t_userintf::send_refer_permission(bool permission) {
evq_trans_layer->push_refer_permission_response(permission);
}
void t_userintf::cb_show_msg(const string &msg, t_msg_priority prio) {
cout << endl;
switch (prio) {
case MSG_NO_PRIO:
break;
case MSG_INFO:
cout << "Info: ";
break;
case MSG_WARNING:
cout << "Warning: ";
break;
case MSG_CRITICAL:
cout << "Critical: ";
break;
default:
cout << "???: ";
}
cout << msg << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
bool t_userintf::cb_ask_msg(const string &msg, t_msg_priority prio) {
// Cannot ask questions in CLI mode.
// Print message and return false
cb_show_msg(msg, prio);
return false;
}
void t_userintf::cb_display_msg(const string &msg, t_msg_priority prio) {
// In CLI mode this is the same as cb_show_msg
cb_show_msg(msg, prio);
}
void t_userintf::cb_log_updated(bool log_zapped) {
// In CLI mode there is no log viewer.
}
void t_userintf::cb_call_history_updated(void) {
// In CLI mode there is no call history viewer.
}
void t_userintf::cb_missed_call(int num_missed_calls) {
// In CLI mode there is no missed call indication.
}
void t_userintf::cb_nat_discovery_progress_start(int num_steps) {
cout << endl;
cout << "Firewall/NAT discovery in progress.\n";
cout << "Please wait.\n";
cout << endl;
}
void t_userintf::cb_nat_discovery_finished(void) {
// Nothing to do in CLI mode.
}
void t_userintf::cb_nat_discovery_progress_step(int step) {
// Nothing to do in CLI mode.
}
bool t_userintf::cb_nat_discovery_cancelled(void) {
// User cannot cancel NAT discovery in CLI mode.
return false;
}
void t_userintf::cb_line_encrypted(int line, bool encrypted, const string &cipher_mode) {
if (line >= NUM_USER_LINES) return;
cout << endl;
if (encrypted) {
cout << "Line " << line + 1 << ": audio encryption enabled (";
cout << cipher_mode << ").\n";
} else {
cout << "Line " << line + 1 << ": audio encryption disabled.\n";
}
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_async_line_encrypted(int line, bool encrypted, const string &cipher_mode) {
t_event_ui *event = new t_event_ui(TYPE_UI_CB_LINE_ENCRYPTED);
MEMMAN_NEW(event);
event->set_line(line);
event->set_encrypted(encrypted);
event->set_cipher_mode(cipher_mode);
evq_ui_events.push(event);
}
void t_userintf::cb_show_zrtp_sas(int line, const string &sas) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": ZRTP SAS = " << sas << endl;
cout << "Confirm the SAS if it is correct.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_async_show_zrtp_sas(int line, const string &sas) {
t_event_ui *event = new t_event_ui(TYPE_UI_CB_SHOW_ZRTP_SAS);
MEMMAN_NEW(event);
event->set_line(line);
event->set_zrtp_sas(sas);
evq_ui_events.push(event);
}
void t_userintf::cb_zrtp_confirm_go_clear(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": remote user disabled encryption.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
phone->pub_zrtp_go_clear_ok(line);
}
void t_userintf::cb_async_zrtp_confirm_go_clear(int line) {
t_event_ui *event = new t_event_ui(TYPE_UI_CB_ZRTP_CONFIRM_GO_CLEAR);
MEMMAN_NEW(event);
event->set_line(line);
evq_ui_events.push(event);
}
void t_userintf::cb_zrtp_sas_confirmed(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": SAS confirmed.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_zrtp_sas_confirmation_reset(int line) {
if (line >= NUM_USER_LINES) return;
cout << endl;
cout << "Line " << line + 1 << ": SAS confirmation reset.\n";
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_update_mwi(void) {
// Nothing to do in CLI mode.
}
void t_userintf::cb_mwi_subscribe_failed(t_user *user_config, t_response *r, bool first_failure) {
// Only report the first failure in a sequence of failures
if (!first_failure) return;
cout << endl;
cout << user_config->get_profile_name();
cout << ", MWI subscription failed: " << r->code << ' ' << r->reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
void t_userintf::cb_mwi_terminated(t_user *user_config, const string &reason) {
cout << endl;
cout << user_config->get_profile_name();
cout << ", MWI subscription terminated: " << reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
bool t_userintf::cb_message_request(t_user *user_config, t_request *r) {
cout << endl;
cout << "Received message\n";
cout << "From:\t\t";
string from_party = format_sip_address(user_config,
r->hdr_from.get_display_presentation(), r->hdr_from.uri);
cout << from_party << endl;
if (r->hdr_organization.is_populated()) {
cout << "Organization:\t" << r->hdr_organization.name << endl;
}
cout << "To:\t\t";
cout << format_sip_address(user_config, r->hdr_to.display, r->hdr_to.uri) << endl;
if (r->hdr_subject.is_populated()) {
cout << "Subject:\t" << r->hdr_subject.subject << endl;
}
cout << endl;
if (r->body && r->body->get_type() == BODY_PLAIN_TEXT)
{
t_sip_body_plain_text *sb = dynamic_cast<t_sip_body_plain_text *>(r->body);
cout << sb->text << endl;
} else if (r->body && r->body->get_type() == BODY_HTML_TEXT) {
t_sip_body_html_text *sb = dynamic_cast<t_sip_body_html_text *>(r->body);
cout << sb->text << endl;
} else {
cout << "Unsupported content type.\n";
}
cout << endl;
cout << CLI_PROMPT;
cout.flush();
// There are no session in CLI mode, so all messages are accepted.
return true;
}
void t_userintf::cb_message_response(t_user *user_config, t_response *r) {
if (r->is_success()) return;
cout << endl;
cout << "Failed to send MESSAGE.\n";
cout << r->code << " " << r->reason << endl;
cout << endl;
cout << CLI_PROMPT;
cout.flush();
}
bool t_userintf::get_last_call_info(t_url &url, string &display,
string &subject, t_user **user_config, bool &hide_user) const
{
if (!last_called_url.is_valid()) return false;
url = last_called_url;
display = last_called_display;
subject = last_called_subject;
*user_config = phone->ref_user_profile(last_called_profile);
hide_user = last_called_hide_user;
return *user_config != NULL;
}
bool t_userintf::can_redial(void) const {
return last_called_url.is_valid() &&
phone->ref_user_profile(last_called_profile) != NULL;
}
void t_userintf::cmd_call(const string &destination, bool immediate) {
string s = "invite ";
s += destination;
exec_command(s);
}
void t_userintf::cmd_quit(void) {
exec_command("quit");
}
void t_userintf::cmd_quit_async(void) {
t_event_ui *event = new t_event_ui(TYPE_UI_CB_QUIT);
MEMMAN_NEW(event);
evq_ui_events.push(event);
}
void t_userintf::cmd_cli(const string &command, bool immediate) {
exec_command(command, immediate);
}
void t_userintf::cmd_show(void) {
// Do nothing in CLI mode.
}
void t_userintf::cmd_hide(void) {
// Do nothing in CLI mode.
}
string t_userintf::get_name_from_abook(t_user *user_config, const t_url &u) {
return ab_local->find_name(user_config, u);
}
void *process_events_main(void *arg) {
ui->process_events();
return NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1