/* Copyright (C) 2005-2009 Michel de Boer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include "call_history.h" #include "log.h" #include "sys_settings.h" #include "translator.h" #include "userintf.h" #include "util.h" // Call history file #define CALL_HISTORY_FILE "twinkle.ch"; // Field seperator in call history file #define REC_SEPARATOR '|' //////////////////////// // class t_call_record //////////////////////// t_mutex t_call_record::mtx_class; unsigned short t_call_record::next_id = 1; t_call_record::t_call_record() { mtx_class.lock(); id = next_id++; if (next_id == 65535) next_id = 1; mtx_class.unlock(); time_start = 0; time_answer = 0; time_end = 0; invite_resp_code = 0; } void t_call_record::renew() { t_mutex_guard x(mutex); mtx_class.lock(); id = next_id++; if (next_id == 65535) next_id = 1; mtx_class.unlock(); time_start = 0; time_answer = 0; time_end = 0; direction = DIR_IN; from_display.clear(); from_uri.set_url(""); from_organization.clear(); to_display.clear(); to_uri.set_url(""); to_organization.clear(); reply_to_display.clear(); reply_to_uri.set_url(""); referred_by_display.clear(); referred_by_uri.set_url(""); subject.clear(); rel_cause = CS_LOCAL_USER; invite_resp_code = 0; invite_resp_reason.clear(); far_end_device.clear(); user_profile.clear(); } void t_call_record::start_call(const t_request *invite, t_direction dir, const string &_user_profile) { assert(invite->method == INVITE); t_mutex_guard x(mutex); struct timeval t; gettimeofday(&t, NULL); time_start = t.tv_sec; from_display = invite->hdr_from.get_display_presentation(); from_uri = invite->hdr_from.uri; if (invite->hdr_organization.is_populated()) { from_organization = invite->hdr_organization.name; } to_display = invite->hdr_to.display; to_uri = invite->hdr_to.uri; if (invite->hdr_reply_to.is_populated()) { reply_to_display = invite->hdr_reply_to.display; reply_to_uri = invite->hdr_reply_to.uri; } if (invite->hdr_referred_by.is_populated()) { referred_by_display = invite->hdr_referred_by.display; referred_by_uri = invite->hdr_referred_by.uri; } if (invite->hdr_subject.is_populated()) { subject = invite->hdr_subject.subject; } direction = dir; user_profile = _user_profile; if (direction == DIR_IN && invite->hdr_user_agent.is_populated()) { far_end_device = invite->hdr_user_agent.get_ua_info(); } } void t_call_record::fail_call(const t_response *resp) { assert(resp->get_class() >= 3); assert(resp->hdr_cseq.method == INVITE); t_mutex_guard x(mutex); struct timeval t; gettimeofday(&t, NULL); time_end = t.tv_sec; rel_cause = CS_FAILURE; invite_resp_code = resp->code; invite_resp_reason = resp->reason; if (resp->hdr_organization.is_populated()) { to_organization = resp->hdr_organization.name; } if (direction == DIR_OUT && resp->hdr_server.is_populated()) { far_end_device = resp->hdr_server.get_server_info(); } } void t_call_record::answer_call(const t_response *resp) { assert(resp->is_success()); t_mutex_guard x(mutex); struct timeval t; gettimeofday(&t, NULL); time_answer = t.tv_sec; invite_resp_code = resp->code; invite_resp_reason = resp->reason; if (resp->hdr_organization.is_populated()) { to_organization = resp->hdr_organization.name; } if (direction == DIR_OUT && resp->hdr_server.is_populated()) { far_end_device = resp->hdr_server.get_server_info(); } } void t_call_record::end_call(t_rel_cause cause) { struct timeval t; t_mutex_guard x(mutex); gettimeofday(&t, NULL); time_end = t.tv_sec; rel_cause = cause; } void t_call_record::end_call(bool far_end) { if (far_end) { end_call(CS_REMOTE_USER); } else { end_call(CS_LOCAL_USER); } } string t_call_record::get_rel_cause(void) const { switch (rel_cause) { case CS_LOCAL_USER: return TRANSLATE2("CoreCallHistory", "local user"); case CS_REMOTE_USER: return TRANSLATE2("CoreCallHistory", "remote user"); case CS_FAILURE: return TRANSLATE2("CoreCallHistory", "failure"); } return TRANSLATE2("CoreCallHistory", "unknown"); } string t_call_record::get_rel_cause_internal(void) const { switch (rel_cause) { case CS_LOCAL_USER: return "local user"; case CS_REMOTE_USER: return "remote user"; case CS_FAILURE: return "failure"; } return "unknown"; } string t_call_record::get_direction(void) const { switch (direction) { case DIR_IN: return TRANSLATE2("CoreCallHistory", "in"); case DIR_OUT: return TRANSLATE2("CoreCallHistory", "out"); } return TRANSLATE2("CoreCallHistory", "unknown"); } string t_call_record::get_direction_internal(void) const { switch (direction) { case DIR_IN: return "in"; case DIR_OUT: return "out"; } return "unknown"; } bool t_call_record::set_rel_cause(const string &cause) { // NOTE: caller and callee were used before version 0.7 // They are still checked here for backward compatibility if (cause == "caller" || cause == "local user") { rel_cause = CS_LOCAL_USER; } else if (cause == "callee" || cause == "remote user") { rel_cause = CS_REMOTE_USER; } else if (cause == "failure") { rel_cause = CS_FAILURE; } else { return false; } return true; } bool t_call_record::set_direction(const string &dir) { if (dir == "in") { direction = DIR_IN; } else if (dir == "out") { direction = DIR_OUT; } else { return false; } return true; } bool t_call_record::create_file_record(vector &v) const { v.clear(); v.push_back(ulong2str(time_start)); v.push_back(ulong2str(time_answer)); v.push_back(ulong2str(time_end)); v.push_back(get_direction_internal()); v.push_back(from_display); v.push_back(from_uri.encode()); v.push_back(from_organization); v.push_back(to_display); v.push_back(to_uri.encode()); v.push_back(to_organization); v.push_back(reply_to_display); v.push_back(reply_to_uri.encode()); v.push_back(referred_by_display); v.push_back(referred_by_uri.encode()); v.push_back(subject); v.push_back(get_rel_cause_internal()); v.push_back(int2str(invite_resp_code)); v.push_back(invite_resp_reason); v.push_back(far_end_device); v.push_back(user_profile); return true; } bool t_call_record::populate_from_file_record(const vector &v) { t_mutex_guard x(mutex); // Check number of fields if (v.size() != 20) return false; time_start = std::stoul(v[0], NULL, 10); time_answer = std::stoul(v[1], NULL, 10); time_end = std::stoul(v[2], NULL, 10); if (!set_direction(v[3])) return false; from_display = v[4]; from_uri.set_url(v[5]); if (!from_uri.is_valid()) return false; from_organization = v[6]; to_display = v[7]; to_uri.set_url(v[8]); if (!to_uri.is_valid()) return false; to_organization = v[9]; reply_to_display = v[10]; reply_to_uri.set_url(v[11]); referred_by_display = v[12]; referred_by_uri.set_url(v[13]); subject = v[14]; if (!set_rel_cause(v[15])) return false; invite_resp_code = atoi(v[16].c_str()); invite_resp_reason = v[17]; far_end_device = v[18]; user_profile = v[19]; return true; } bool t_call_record::is_valid(void) const { if (time_start == 0 || time_end == 0) return false; if (time_answer > 0 && rel_cause == CS_FAILURE) return false; return true; } unsigned short t_call_record::get_id(void) const { return id; } t_call_record::t_call_record(const t_call_record& that) { *this = that; } t_call_record& t_call_record::operator=(const t_call_record& that) { t_mutex_guard x1(that.mutex); t_mutex_guard x2(this->mutex); id = that.id; time_start = that.time_start; time_answer = that.time_answer; time_end = that.time_end; direction = that.direction; from_display = that.from_display; from_uri = that.from_uri; from_organization = that.from_organization; to_display = that.to_display; to_uri = that.to_uri; to_organization = that.to_organization; reply_to_display = that.reply_to_display; reply_to_uri = that.reply_to_uri; referred_by_display = that.referred_by_display; referred_by_uri = that.referred_by_uri; subject = that.subject; rel_cause = that.rel_cause; invite_resp_code = that.invite_resp_code; invite_resp_reason = that.invite_resp_reason; far_end_device = that.far_end_device; user_profile = that.user_profile; return *this; } //////////////////////// // class t_call_history //////////////////////// t_call_history::t_call_history() : utils::t_record_file() { set_header("time_start|time_answer|time_end|direction|from_display|from_uri|" "from_organization|to_display|to_uri|to_organization|" "reply_to_display|reply_to_uri|referred_by_display|referred_by_uri|" "subject|rel_cause|invite_resp_code|invite_resp_reason|" "far_end_device|user_profile"); set_separator(REC_SEPARATOR); string s(DIR_HOME); s += "/"; s += USER_DIR; s += "/"; s += CALL_HISTORY_FILE; set_filename(s); num_missed_calls = 0; } void t_call_history::add_call_record(const t_call_record &call_record, bool write) { if (!call_record.is_valid()) { log_file->write_report("Call history record is not valid.", "t_call_history::add_call_record", LOG_NORMAL, LOG_WARNING); return; } mtx_records.lock(); records.push_back(call_record); while (records.size() > (size_t)sys_config->get_ch_max_size()) { records.pop_front(); } // Increment missed calls counter if (call_record.rel_cause == t_call_record::CS_FAILURE && call_record.direction == t_call_record::DIR_IN) { ++num_missed_calls; ui->cb_missed_call(num_missed_calls); } mtx_records.unlock(); if (write) { string msg; if (!save(msg)) { log_file->write_report(msg, "t_call_history::add_call_record", LOG_NORMAL, LOG_WARNING); } } // Update call history in user interface. ui->cb_call_history_updated(); } void t_call_history::delete_call_record(unsigned short id, bool write) { mtx_records.lock(); for (list::iterator i = records.begin(); i != records.end(); i++) { if (i->get_id() == id) { records.erase(i); break; } } mtx_records.unlock(); if (write) { string msg; if (!save(msg)) { log_file->write_report(msg, "t_call_history::delete_call_record", LOG_NORMAL, LOG_WARNING); } } // Update call history in user interface. ui->cb_call_history_updated(); } void t_call_history::get_history(list &history) { mtx_records.lock(); history = records; mtx_records.unlock(); } void t_call_history::clear(bool write) { mtx_records.lock(); records.clear(); mtx_records.unlock(); if (write) { string msg; if (!save(msg)) { log_file->write_report(msg, "t_call_history::clear", LOG_NORMAL, LOG_WARNING); } } // Update call history in user interface. ui->cb_call_history_updated(); clear_num_missed_calls(); } int t_call_history::get_num_missed_calls(void) const { return num_missed_calls; } void t_call_history::clear_num_missed_calls(void) { mtx_records.lock(); num_missed_calls = 0; mtx_records.unlock(); ui->cb_missed_call(0); }