/* 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 "buddy.h" #include #include "log.h" #include "phone.h" #include "phone_user.h" #include "userintf.h" #include "audits/memman.h" extern t_phone *phone; extern t_event_queue *evq_timekeeper; /** Buddy */ void t_buddy::cleanup_presence_dialog(void) { assert(phone_user); if (presence_dialog && presence_dialog->get_subscription_state() == SS_TERMINATED) { string reason_termination = presence_dialog->get_reason_termination(); bool may_resubscribe = presence_dialog->get_may_resubscribe(); unsigned long dur_resubscribe = presence_dialog->get_resubscribe_after(); MEMMAN_DELETE(presence_dialog); delete presence_dialog; presence_dialog = NULL; phone_user->stun_binding_inuse_presence--; phone_user->cleanup_stun_data(); phone_user->cleanup_nat_keepalive(); if (presence_auto_resubscribe) { if (may_resubscribe) { if (dur_resubscribe > 0) { start_resubscribe_presence_timer(dur_resubscribe * 1000); } else { subscribe_presence(); } } else if (reason_termination.empty()) { start_resubscribe_presence_timer(DUR_PRESENCE_FAILURE * 1000); } } } } t_buddy::t_buddy() : phone_user(NULL), may_subscribe_presence(false), presence_state(this), presence_dialog(NULL), subscribe_after_stun(false), presence_auto_resubscribe(false), delete_after_presence_terminated(false), id_resubscribe_presence(0) { } t_buddy::t_buddy(t_phone_user *_phone_user) : phone_user(_phone_user), may_subscribe_presence(false), presence_state(this), presence_dialog(NULL), subscribe_after_stun(false), presence_auto_resubscribe(false), delete_after_presence_terminated(false), id_resubscribe_presence(0) { } t_buddy::t_buddy(t_phone_user *_phone_user, const string _name, const string &_sip_address) : phone_user(_phone_user), name(_name), sip_address(_sip_address), may_subscribe_presence(false), presence_state(this), presence_dialog(NULL), subscribe_after_stun(false), presence_auto_resubscribe(false), delete_after_presence_terminated(false), id_resubscribe_presence(0) { } t_buddy::t_buddy(const t_buddy &other) : phone_user(other.phone_user), name(other.name), sip_address(other.sip_address), may_subscribe_presence(other.may_subscribe_presence), presence_state(this), presence_dialog(NULL), subscribe_after_stun(false), presence_auto_resubscribe(false), delete_after_presence_terminated(false), id_resubscribe_presence(0) {} t_buddy::~t_buddy() { if (presence_dialog) { MEMMAN_DELETE(presence_dialog); delete presence_dialog; } } string t_buddy::get_name(void) const { return name; } string t_buddy::get_sip_address(void) const { return sip_address; } bool t_buddy::get_may_subscribe_presence(void) const { return may_subscribe_presence; } const t_presence_state *t_buddy::get_presence_state(void) const { return &presence_state; } t_user *t_buddy::get_user_profile(void) { assert(phone_user); return phone_user->get_user_profile(); } t_buddy_list *t_buddy::get_buddy_list(void) { assert(phone_user); return phone_user->get_buddy_list(); } void t_buddy::set_phone_user(t_phone_user *_phone_user) { phone_user = _phone_user; } void t_buddy::set_name(const string &_name) { name = _name; notify(); } void t_buddy::set_sip_address(const string &_sip_address) { sip_address = _sip_address; notify(); } void t_buddy::set_may_subscribe_presence(bool _may_subscribe_presence) { may_subscribe_presence = _may_subscribe_presence; notify(); } bool t_buddy::match_response(t_response *r, t_tuid tuid) const { return (presence_dialog && presence_dialog->match_response(r, tuid)); } bool t_buddy::match_request(t_request *r) const { if (!presence_dialog) return false; bool partial_match = false; bool match = presence_dialog->match_request(r, partial_match); if (match) return true; if (partial_match && presence_dialog->get_remote_tag().empty()) { // A NOTIFY may be received before a 2XX on SUBSCRIBE. // In this case the NOTIFY will establish the dialog. return true; } return false; } bool t_buddy::match_timer(t_subscribe_timer timer, t_object_id id_timer) const { if (presence_dialog && presence_dialog->match_timer(timer, id_timer)) { return true; } return id_timer == id_resubscribe_presence; } void t_buddy::timeout(t_subscribe_timer timer, t_object_id id_timer) { switch (timer) { case STMR_SUBSCRIPTION: if (presence_dialog && presence_dialog->match_timer(timer, id_timer)) { (void)presence_dialog->timeout(timer); cleanup_presence_dialog(); } else if (id_timer == id_resubscribe_presence) { // Try to subscribe to presence id_resubscribe_presence = 0; subscribe_presence(); } break; default: assert(false); } } void t_buddy::recvd_response(t_response *r, t_tuid tuid, t_tid tid) { if (presence_dialog) { presence_dialog->recvd_response(r, tuid, tid); cleanup_presence_dialog(); } } void t_buddy::recvd_request(t_request *r, t_tuid tuid, t_tid tid) { if (presence_dialog) { presence_dialog->recvd_request(r, tuid, tid); cleanup_presence_dialog(); } } void t_buddy::start_resubscribe_presence_timer(unsigned long duration) { t_tmr_subscribe *t; t = new t_tmr_subscribe(duration, STMR_SUBSCRIPTION, 0, 0, SIP_EVENT_PRESENCE, ""); MEMMAN_NEW(t); id_resubscribe_presence = t->get_object_id(); evq_timekeeper->push_start_timer(t); MEMMAN_DELETE(t); delete t; } void t_buddy::stop_resubscribe_presence_timer(void) { if (id_resubscribe_presence != 0) { evq_timekeeper->push_stop_timer(id_resubscribe_presence); id_resubscribe_presence = 0; } } void t_buddy::stun_completed(void) { if (subscribe_after_stun) { subscribe_after_stun = false; subscribe_presence(); } } void t_buddy::stun_failed(void) { if (subscribe_after_stun) { subscribe_after_stun = false; start_resubscribe_presence_timer(DUR_PRESENCE_FAILURE * 1000); } } void t_buddy::subscribe_presence(void) { assert(phone_user); t_user *user_config = phone_user->get_user_profile(); if (!may_subscribe_presence) return; presence_auto_resubscribe = true; if (presence_dialog) { // Already subscribed. log_file->write_header("t_buddy::subscribe_presence", LOG_NORMAL, LOG_DEBUG); log_file->write_raw("Already subscribed to presence: "); log_file->write_raw(name); log_file->write_raw(", "); log_file->write_raw(sip_address); log_file->write_endl(); log_file->write_footer(); return; } // If STUN is enabled, then do a STUN query before registering to // determine the public IP address. if (phone_user->use_stun) { if (phone_user->stun_public_ip_sip == 0) { phone_user->send_stun_request(); phone_user->presence_subscribe_after_stun = true; subscribe_after_stun = true; return; } phone_user->stun_binding_inuse_presence++; } presence_dialog = new t_presence_dialog(phone_user, &presence_state); MEMMAN_NEW(presence_dialog); string dest = ui->expand_destination(user_config, sip_address); t_url dest_url(dest); if (!dest_url.is_valid()) { log_file->write_header("t_buddy::subscribe_presence", LOG_NORMAL, LOG_WARNING); log_file->write_raw("Invalid SIP address: "); log_file->write_raw(sip_address); log_file->write_endl(); log_file->write_footer(); return; } presence_dialog->subscribe(DUR_PRESENCE(user_config), dest_url, dest_url, ""); // Start sending NAT keepalive packets when STUN is used // (or in case of symmetric firewall) if (phone_user->use_nat_keepalive && phone_user->id_nat_keepalive == 0) { // Just start the NAT keepalive timer. The SUBSCRIBE // message will create the NAT binding. So there is // no need to send a NAT keep alive packet now. phone->start_timer(PTMR_NAT_KEEPALIVE, phone_user); } cleanup_presence_dialog(); } void t_buddy::unsubscribe_presence(bool remove) { presence_auto_resubscribe = false; stop_resubscribe_presence_timer(); presence_state.set_basic_state(t_presence_state::ST_BASIC_UNKNOWN); delete_after_presence_terminated = remove; if (presence_dialog) { presence_dialog->unsubscribe(); cleanup_presence_dialog(); } } bool t_buddy::create_file_record(vector &v) const { if (delete_after_presence_terminated) return false; v.clear(); v.push_back(name); v.push_back(sip_address); v.push_back((may_subscribe_presence ? "y" : "n")); return true; } bool t_buddy::populate_from_file_record(const vector &v) { if (v.size() !=3 ) return false; name = v[0]; sip_address = v[1]; may_subscribe_presence = (v[2] == "y"); return true; } bool t_buddy::operator==(const t_buddy &other) const { return (name == other.name && sip_address == other.sip_address); } void t_buddy::clear_presence(void) { if (id_resubscribe_presence) stop_resubscribe_presence_timer(); if (presence_dialog) { MEMMAN_DELETE(presence_dialog); delete presence_dialog; presence_dialog = NULL; } presence_state.set_basic_state(t_presence_state::ST_BASIC_UNKNOWN); } bool t_buddy::is_presence_terminated(void) const { return presence_dialog == NULL; } bool t_buddy::must_delete_now(void) const { return delete_after_presence_terminated && is_presence_terminated(); } /** Buddy list */ void t_buddy_list::add_record(const t_buddy &record) { t_buddy r(record); r.set_phone_user(phone_user); utils::t_record_file::add_record(r); } t_buddy_list::t_buddy_list(t_phone_user *_phone_user) : phone_user(_phone_user), is_subscribed(false) { t_user *user_config = phone_user->get_user_profile(); set_header("name|sip_address|subscribe"); set_separator('|'); string filename = user_config->get_profile_name() + BUDDY_FILE_EXT; string f = user_config->expand_filename(filename); set_filename(f); } t_user *t_buddy_list::get_user_profile(void) { return phone_user->get_user_profile(); } t_buddy *t_buddy_list::add_buddy(const t_buddy &buddy) { t_buddy *b = NULL; mtx_records.lock(); add_record(buddy); // KLUDGE: this code assumes that the buddy is added at the end. b = &records.back(); mtx_records.unlock(); log_file->write_header("t_buddy_list::add_buddy"); log_file->write_raw("Added buddy: "); log_file->write_raw(b->get_name()); log_file->write_raw(", "); log_file->write_raw(b->get_sip_address()); log_file->write_endl(); log_file->write_footer(); return b; } void t_buddy_list::del_buddy(const t_buddy &buddy) { mtx_records.lock(); list::iterator it = find(records.begin(), records.end(), buddy); if (it == records.end()) { mtx_records.unlock(); return; } log_file->write_header("t_buddy_list::del_buddy"); log_file->write_raw("Delete buddy: "); log_file->write_raw(buddy.get_name()); log_file->write_raw(", "); log_file->write_raw(buddy.get_sip_address()); log_file->write_endl(); log_file->write_footer(); records.erase(it); mtx_records.unlock(); } bool t_buddy_list::match_response(t_response *r, t_tuid tuid, t_buddy **buddy) { *buddy = NULL; mtx_records.lock(); for (list::iterator it = records.begin(); it != records.end(); ++it) { if (it->match_response(r, tuid)) { *buddy = &(*it); break; } } mtx_records.unlock(); return *buddy != NULL; } bool t_buddy_list::match_request(t_request *r, t_buddy **buddy) { *buddy = NULL; mtx_records.lock(); for (list::iterator it = records.begin(); it != records.end(); ++it) { if (it->match_request(r)) { *buddy = &(*it); break; } } mtx_records.unlock(); return *buddy != NULL; } bool t_buddy_list::match_timer(t_subscribe_timer timer, t_object_id id_timer, t_buddy **buddy) { *buddy = NULL; mtx_records.lock(); for (list::iterator it = records.begin(); it != records.end(); ++it) { if (it->match_timer(timer, id_timer)) { *buddy = &(*it); break; } } mtx_records.unlock(); return *buddy != NULL; } void t_buddy_list::stun_completed(void) { mtx_records.lock(); for (list::iterator it = records.begin(); it != records.end(); ++it) { it->stun_completed(); } mtx_records.unlock(); } void t_buddy_list::stun_failed(void) { mtx_records.lock(); for (list::iterator it = records.begin(); it != records.end(); ++it) { it->stun_failed(); } mtx_records.unlock(); } void t_buddy_list::subscribe_presence(void) { mtx_records.lock(); for (list::iterator it = records.begin(); it != records.end(); ++it) { it->subscribe_presence(); } is_subscribed = true; mtx_records.unlock(); } void t_buddy_list::unsubscribe_presence(void) { mtx_records.lock(); for (list::iterator it = records.begin(); it != records.end(); ++it) { it->unsubscribe_presence(); } is_subscribed = false; mtx_records.unlock(); } bool t_buddy_list::get_is_subscribed() const { bool result; mtx_records.lock(); result = is_subscribed; mtx_records.unlock(); return result; } void t_buddy_list::clear_presence(void) { mtx_records.lock(); for (list::iterator it = records.begin(); it != records.end(); ++it) { it->clear_presence(); } is_subscribed = false; mtx_records.unlock(); } bool t_buddy_list::is_presence_terminated(void) const { bool result = true; mtx_records.lock(); for (list::const_iterator it = records.begin(); it != records.end(); ++it) { if (!it->is_presence_terminated()) { result = false; break; } } mtx_records.unlock(); return result; }