From e2bc6f4153813cc570ae814c8ddb74628009b488 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Mon, 13 Apr 2015 09:21:39 +0200 Subject: initial checkin Check in contents of upstream 1.4.2 tarball, exclude generated files. --- src/presence/buddy.cpp | 574 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 574 insertions(+) create mode 100644 src/presence/buddy.cpp (limited to 'src/presence/buddy.cpp') diff --git a/src/presence/buddy.cpp b/src/presence/buddy.cpp new file mode 100644 index 0000000..075626a --- /dev/null +++ b/src/presence/buddy.cpp @@ -0,0 +1,574 @@ +/* + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#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; +} -- cgit v1.2.3