diff options
author | Michal Kubecek <mkubecek@suse.cz> | 2015-04-13 09:21:39 +0200 |
---|---|---|
committer | Michal Kubecek <mkubecek@suse.cz> | 2015-04-13 09:21:39 +0200 |
commit | e2bc6f4153813cc570ae814c8ddb74628009b488 (patch) | |
tree | a40b171be1d859c2232ccc94f758010f9ae54d3c /src/presence | |
download | twinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.tar twinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.tar.gz twinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.tar.lz twinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.tar.xz twinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.zip |
initial checkin
Check in contents of upstream 1.4.2 tarball, exclude generated files.
Diffstat (limited to 'src/presence')
-rw-r--r-- | src/presence/Makefile.am | 17 | ||||
-rw-r--r-- | src/presence/buddy.cpp | 574 | ||||
-rw-r--r-- | src/presence/buddy.h | 341 | ||||
-rw-r--r-- | src/presence/pidf_body.cpp | 221 | ||||
-rw-r--r-- | src/presence/pidf_body.h | 104 | ||||
-rw-r--r-- | src/presence/presence_dialog.cpp | 35 | ||||
-rw-r--r-- | src/presence/presence_dialog.h | 47 | ||||
-rw-r--r-- | src/presence/presence_epa.cpp | 71 | ||||
-rw-r--r-- | src/presence/presence_epa.h | 67 | ||||
-rw-r--r-- | src/presence/presence_state.cpp | 100 | ||||
-rw-r--r-- | src/presence/presence_state.h | 94 | ||||
-rw-r--r-- | src/presence/presence_subscription.cpp | 144 | ||||
-rw-r--r-- | src/presence/presence_subscription.h | 51 |
13 files changed, 1866 insertions, 0 deletions
diff --git a/src/presence/Makefile.am b/src/presence/Makefile.am new file mode 100644 index 0000000..17caf93 --- /dev/null +++ b/src/presence/Makefile.am @@ -0,0 +1,17 @@ +AM_CPPFLAGS = -Wall $(CCRTP_CFLAGS) $(XML2_CFLAGS) -I$(top_srcdir)/src + +noinst_LIBRARIES = libpresence.a + +libpresence_a_SOURCES =\ + buddy.cpp\ + pidf_body.cpp\ + presence_dialog.cpp\ + presence_epa.cpp\ + presence_state.cpp\ + presence_subscription.cpp\ + buddy.h\ + pidf_body.h\ + presence_dialog.h\ + presence_epa.h\ + presence_state.h\ + presence_subscription.h 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 <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 "buddy.h" + +#include <cassert> + +#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<string> &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<string> &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<t_buddy>::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<t_buddy>::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<t_buddy>::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<t_buddy>::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<t_buddy>::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<t_buddy>::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<t_buddy>::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<t_buddy>::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<t_buddy>::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<t_buddy>::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<t_buddy>::const_iterator it = records.begin(); it != records.end(); ++it) { + if (!it->is_presence_terminated()) { + result = false; + break; + } + } + + mtx_records.unlock(); + + return result; +} diff --git a/src/presence/buddy.h b/src/presence/buddy.h new file mode 100644 index 0000000..b7852de --- /dev/null +++ b/src/presence/buddy.h @@ -0,0 +1,341 @@ +/* + Copyright (C) 2005-2009 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 +*/ + +/** + * @file + * Buddy list + */ + +#ifndef _BUDDY_H +#define _BUDDY_H + +#include <string> +#include <set> + +#include "presence_state.h" +#include "presence_dialog.h" + +#include "sockets/url.h" +#include "utils/record_file.h" +#include "patterns/observer.h" + +// Forward declaration +class t_phone_user; +class t_buddy_list; + +#define BUDDY_FILE_EXT ".bud" + +using namespace std; + +/** Buddy */ +class t_buddy : public utils::t_record, public patterns::t_subject { +private: + /** Phone user owning this buddy. */ + t_phone_user *phone_user; + + /** Name of buddy (for display only) */ + string name; + + /** SIP address of the buddy. */ + string sip_address; + + /** Indicates if the user may subscribe to the presence state of the buddy. */ + bool may_subscribe_presence; + + /** Presence state. */ + t_presence_state presence_state; + + /** Presence subscription dialog. */ + t_presence_dialog *presence_dialog; + + /** Subscribe to presence after STUN transaction completed. */ + bool subscribe_after_stun; + + /** + * Indicates if presence must be automatically resubscribed to, if the + * subscription terminates with a reason telling that resubscription + * is possible. + */ + bool presence_auto_resubscribe; + + /** + * Indicates if the buddy must be deleted after the presence subscription + * has been terminated. + */ + bool delete_after_presence_terminated; + + /** Handle presence dialog termination. */ + void cleanup_presence_dialog(void); + +public: + /** Interval before trying to resubscribe to presence after a failure. */ + t_object_id id_resubscribe_presence; + + /** Constructor. */ + t_buddy(); + + /** Constructor. */ + t_buddy(t_phone_user *_phone_user); + + /** Constructor. */ + t_buddy(t_phone_user *_phone_user, const string _name, const string &_sip_address); + + /** Copy constructor. */ + t_buddy(const t_buddy &other); + + /** Destructor. */ + virtual ~t_buddy(); + + /** @name Getters */ + //@{ + string get_name(void) const; + string get_sip_address(void) const; + bool get_may_subscribe_presence(void) const; + const t_presence_state *get_presence_state(void) const; + //@} + + /** + * Get user profile for the user owning this buddy. + * @return User profile. + */ + t_user *get_user_profile(void); + + /** + * Get the buddy list containing this buddy. + * @return Buddy list + */ + t_buddy_list *get_buddy_list(void); + + /** @name Setters */ + //@{ + void set_phone_user(t_phone_user *_phone_user); + void set_name(const string &_name); + void set_sip_address(const string &_sip_address); + void set_may_subscribe_presence(bool _may_subscribe_presence); + //@} + + /** + * Match response with a buddy. It matches if the buddy + * has a presence dialog that matches with the response. + * @param r [in] The response. + * @param tuid [in] Transaction user id. + * @return True if the response matches, otherwise false. + */ + bool match_response(t_response *r, t_tuid tuid) const; + + /** + * Match request with buddy list. It matches if a buddy in the list + * has a presence dialog that matches with the request. + * @param r [in] The request. + * @return True if the request matches, otherwise false. + */ + bool match_request(t_request *r) const; + + /** + * Match a timer id with a running timer. + * @param timer [in] The running timer. + * @param id_timer [in] The timer id. + * @return true, if timer id matches with timer. + * @return false, otherwise. + */ + bool match_timer(t_subscribe_timer timer, t_object_id id_timer) const; + + /** + * Process timeout. + * @param timer [in] The timer that expired. + * @param id_timer [in] The timer id. + */ + void timeout(t_subscribe_timer timer, t_object_id id_timer); + + /** + * Handle received response. + * @param r [in] The response. + * @param tuid [in] Transaction user id. + * @param tid [in] Transaction id. + */ + void recvd_response(t_response *r, t_tuid tuid, t_tid tid); + + /** + * Handle received request. + * @param r [in] The request. + * @param tuid [in] Transaction user id. + * @param tid [in] Transaction id. + */ + void recvd_request(t_request *r, t_tuid tuid, t_tid tid); + + /** + * Start the re-subscribe timer after a presence subscription failure. + * @param duration [in] Duration before trying a re-subscribe (s) + */ + void start_resubscribe_presence_timer(unsigned long duration); + + /** Stop presence re-subscribe timer. */ + void stop_resubscribe_presence_timer(void); + + /** + * By calling this method, succesful STUN completion is signalled to + * the buddy. It will subscribe to presence if it was waiting for STUN. + */ + void stun_completed(void); + + /** + * By calling this method, a STUN failure is signalled to + * the buddy. It will reschedule a presence subscription if it is + * waiting for STUN to complete. + */ + void stun_failed(void); + + /** Subscribe to presence of the buddy if we may do so. */ + void subscribe_presence(void); + + /** Unsubscribe to presence. + * @param remove [in] Indicates if the buddy must be deleted after unsubscription. + */ + void unsubscribe_presence(bool remove = false); + + virtual bool create_file_record(vector<string> &v) const; + virtual bool populate_from_file_record(const vector<string> &v); + + /** Compare 2 buddies for equality (same SIP address) */ + bool operator==(const t_buddy &other) const; + + /** Clear presence state. */ + void clear_presence(void); + + /** + * Check if presence subscription is terminated. + * @return true, if presence subscriptions is terminated. + * @return false, otherwise + */ + bool is_presence_terminated(void) const; + + /** + * Check if buddy must be deleted. + * @return true, if the buddy must be deleted immediately. + * @return false, otherwise. + */ + bool must_delete_now(void) const; +}; + +/** List of buddies for a particular account. */ +class t_buddy_list : public utils::t_record_file<t_buddy> { +private: + /** Phone user owning this buddy list. */ + t_phone_user *phone_user; + + /** + * Indicates if subscribe is done. This indicator will be set to false + * when you call unsubscribe. + */ + bool is_subscribed; + +protected: + virtual void add_record(const t_buddy &record); + +public: + /** Constructor. */ + t_buddy_list(t_phone_user *_phone_user); + + /** + * Get the user profile for this buddy list. + * @return User profile. + */ + t_user *get_user_profile(void); + + /** + * Add a buddy. + * @param buddy [in] Buddy to add. + * @return Pointer to added buddy. + * @note This method adds a copy of the buddy. It returns a pointer to this copy. + */ + t_buddy *add_buddy(const t_buddy &buddy); + + /** + * Delete a buddy. + * @param buddy [in] Buddy to delete. + */ + void del_buddy(const t_buddy &buddy); + + /** + * Match response with buddy list. It matches if a buddy in the list + * has a presence dialog that matches with the response. + * @param r [in] The response. + * @param tuid [in] Transaction user id. + * @param buddy [out] On a match, this parameter contains the matching buddy. + * @return True if the response matches, otherwise false. + */ + bool match_response(t_response *r, t_tuid tuid, t_buddy **buddy); + + /** + * Match request with buddy list. It matches if a buddy in the list + * has a presence dialog that matches with the request. + * @param r [in] The request. + * @param buddy [out] On a match, this parameter contains the matching buddy. + * @return True if the request matches, otherwise false. + */ + bool match_request(t_request *r, t_buddy **buddy); + + /** + * Match a timer id with a running timer. A timer id matches with the + * buddy list if it matches with one of the buddies in the list. + * @param timer [in] The running timer. + * @param id_timer [in] The timer id. + * @param buddy [out] On a match, this parameter contains the matching buddy. + * @return true, if timer id matches with timer. + * @return false, otherwise. + */ + bool match_timer(t_subscribe_timer timer, t_object_id id_timer, t_buddy **buddy); + + /** + * By calling this method, succesful STUN completion is signalled to the buddy + * list. The buddy list will now start presence subscriptions that were waiting + * for STUN to complete. + */ + void stun_completed(void); + + /** + * By calling this method, a STUN failure is signalled to the buddy list. + * The buddy list will reschedule presence subscriptions that were waiting + * for STUN to complete. + */ + void stun_failed(void); + + /** Subscribe to presence of all buddies in the list. */ + void subscribe_presence(void); + + /** Unsubscribe to presence of all buddies in the list. */ + void unsubscribe_presence(void); + + /** + * Check if user is subcribed to buddy list presence. + * @return True if subscribed, otherwise false. + */ + bool get_is_subscribed() const; + + /** Clear presence state of all buddies. */ + void clear_presence(void); + + /** + * Check if all presence subscriptions are terminated. + * @return true, if all presence subscriptions are terminated. + * @return false, otherwise + */ + bool is_presence_terminated(void) const; +}; + +#endif diff --git a/src/presence/pidf_body.cpp b/src/presence/pidf_body.cpp new file mode 100644 index 0000000..aa67401 --- /dev/null +++ b/src/presence/pidf_body.cpp @@ -0,0 +1,221 @@ +/* + Copyright (C) 2005-2009 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 "pidf_body.h" + +#include <cassert> +#include <libxml/parser.h> + +#include "log.h" +#include "util.h" +#include "audits/memman.h" + +#define PIDF_XML_VERSION "1.0" +#define PIDF_NAMESPACE "urn:ietf:params:xml:ns:pidf" + +#define IS_PIDF_TAG(node, tag) IS_XML_TAG(node, tag, PIDF_NAMESPACE) + +#define IS_PIDF_ATTR(attr, attr_name) IS_XML_ATTR(attr, attr_name, PIDF_NAMESPACE) + +bool t_pidf_xml_body::extract_status(void) { + assert(xml_doc); + + xmlNode *root_element = NULL; + + // Get root + root_element = xmlDocGetRootElement(xml_doc); + if (!root_element) { + log_file->write_report("PIDF document has no root element.", + "t_pidf_xml_body::extract_status", + LOG_NORMAL, LOG_WARNING); + return false; + } + + // Check if root is <presence> + if (!IS_PIDF_TAG(root_element, "presence")) { + log_file->write_report("PIDF document has invalid root element.", + "t_pidf_xml_body::extract_status", + LOG_NORMAL, LOG_WARNING); + return false; + } + + pres_entity.clear(); + tuple_id.clear(); + basic_status.clear(); + + // Get presence entity + xmlChar *prop_entity = xmlGetProp(root_element, BAD_CAST "entity"); + if (prop_entity) { + pres_entity = (char *)prop_entity; + } else { + log_file->write_report("Presence entity is missing.", + "t_pidf_xml_body::extract_status", + LOG_NORMAL, LOG_WARNING); + } + + xmlNode *child = root_element->children; + + // Process children of root. + for (xmlNode *cur_node = child; cur_node; cur_node = cur_node->next) { + // Process tuple + if (IS_PIDF_TAG(cur_node, "tuple")) { + process_pidf_tuple(cur_node); + + // Process the first tuple and then stop. + // Currently there is no support for multiple tuples + // or additional elements. + break; + } + } + + return true; +} + +void t_pidf_xml_body::process_pidf_tuple(xmlNode *tuple) { + assert(tuple); + + // Get tuple id. + xmlChar *id = xmlGetProp(tuple, BAD_CAST "id"); + if (id) { + tuple_id = (char *)id; + } else { + log_file->write_report("Tuple id is missing.", + "t_pidf_xml_body::process_pidf_tuple", + LOG_NORMAL, LOG_WARNING); + } + + // Find status element + xmlNode *child = tuple->children; + for (xmlNode *cur_node = child; cur_node; cur_node = cur_node->next) { + // Process status + if (IS_PIDF_TAG(cur_node, "status")) { + process_pidf_status(cur_node); + break; + } + } +} + +void t_pidf_xml_body::process_pidf_status(xmlNode *status) { + assert(status); + + xmlNode *child = status->children; + for (xmlNode *cur_node = child; cur_node; cur_node = cur_node->next) { + // Process status + if (IS_PIDF_TAG(cur_node, "basic")) { + process_pidf_basic(cur_node); + break; + } + } +} + +void t_pidf_xml_body::process_pidf_basic(xmlNode *basic) { + assert(basic); + + xmlNode *child = basic->children; + if (child && child->type == XML_TEXT_NODE) { + basic_status = tolower((char*)child->content); + } else { + log_file->write_report("<basic> element has no content.", + "t_pidf_xml_body::process_pidf_basic", + LOG_NORMAL, LOG_WARNING); + } +} + +void t_pidf_xml_body::create_xml_doc(const string &xml_version, const string &charset) { + t_sip_body_xml::create_xml_doc(xml_version, charset); + + // presence + xmlNode *node_presence = xmlNewNode(NULL, BAD_CAST "presence"); + xmlNs *ns_pidf = xmlNewNs(node_presence, BAD_CAST PIDF_NAMESPACE, NULL); + xmlNewProp(node_presence, BAD_CAST "entity", BAD_CAST pres_entity.c_str()); + xmlDocSetRootElement(xml_doc, node_presence); + + // tuple + xmlNode *node_tuple = xmlNewChild(node_presence, ns_pidf, BAD_CAST "tuple", NULL); + xmlNewProp(node_tuple, BAD_CAST "id", BAD_CAST tuple_id.c_str()); + + // status + xmlNode *node_status = xmlNewChild(node_tuple, ns_pidf, BAD_CAST "status", NULL); + + // basic + xmlNewChild(node_status, ns_pidf, + BAD_CAST "basic", BAD_CAST basic_status.c_str()); +} + +t_pidf_xml_body::t_pidf_xml_body() : t_sip_body_xml () +{} + +t_sip_body *t_pidf_xml_body::copy(void) const { + t_pidf_xml_body *body = new t_pidf_xml_body(*this); + MEMMAN_NEW(body); + + // Clear the xml_doc pointer in the new body, as a copy of the + // XML document must be copied to the body. + body->xml_doc = NULL; + + copy_xml_doc(body); + + return body; +} + +t_body_type t_pidf_xml_body::get_type(void) const { + return BODY_PIDF_XML; +} + +t_media t_pidf_xml_body::get_media(void) const { + return t_media("application", "pidf+xml"); +} + +string t_pidf_xml_body::get_pres_entity(void) const { + return pres_entity; +} + +string t_pidf_xml_body::get_tuple_id(void) const { + return tuple_id; +} + +string t_pidf_xml_body::get_basic_status(void) const { + return basic_status; +} + +void t_pidf_xml_body::set_pres_entity(const string &_pres_entity) { + clear_xml_doc(); + pres_entity = _pres_entity; +} + +void t_pidf_xml_body::set_tuple_id(const string &_tuple_id) { + clear_xml_doc(); + tuple_id = _tuple_id; +} + +void t_pidf_xml_body::set_basic_status(const string &_basic_status) { + clear_xml_doc(); + basic_status = _basic_status; +} + +bool t_pidf_xml_body::parse(const string &s) { + if (t_sip_body_xml::parse(s)) { + if (!extract_status()) { + MEMMAN_DELETE(xml_doc); + xmlFreeDoc(xml_doc); + xml_doc = NULL; + } + } + + return (xml_doc != NULL); +} diff --git a/src/presence/pidf_body.h b/src/presence/pidf_body.h new file mode 100644 index 0000000..445d531 --- /dev/null +++ b/src/presence/pidf_body.h @@ -0,0 +1,104 @@ +/* + Copyright (C) 2005-2009 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 +*/ + +/** + * @file + * RFC 3863 pidf+xml body + */ + +#ifndef _PIDF_BODY_H +#define _PIDF_BODY_H + +#include <string> +#include <libxml/tree.h> +#include "parser/sip_body.h" + +#define PIDF_STATUS_BASIC_OPEN "open" +#define PIDF_STATUS_BASIC_CLOSED "closed" + +/** RFC 3863 pidf+xml body */ +class t_pidf_xml_body : public t_sip_body_xml { +private: + string pres_entity; /**< Presence entity */ + string tuple_id; /**< Id of tuple containing the basic status. */ + string basic_status; /**< Value of basic-tag */ + + /** + * Extract the status information from a PIDF document. + * This will populate the state attributes. + * @return True if PIDF document is valid, otherwise false. + * @pre The @ref pidf_doc should contain a valid PIDF document. + */ + bool extract_status(void); + + /** + * Process tuple element. + * @param tuple [in] tuple element. + */ + void process_pidf_tuple(xmlNode *tuple); + + /** + * Process status element. + * @param status [in] status element. + */ + void process_pidf_status(xmlNode *status); + + /** + * Process basic element. + * @param basic [in] basic element. + */ + void process_pidf_basic(xmlNode *basic); + +protected: + /** + * Create a pidf document from the values stored in the attributes. + */ + virtual void create_xml_doc(const string &xml_version = "1.0", const string &charset = "UTF-8"); + +public: + /** Constructor */ + t_pidf_xml_body(); + + virtual t_sip_body *copy(void) const; + virtual t_body_type get_type(void) const; + virtual t_media get_media(void) const; + + /** @name Getters */ + //@{ + string get_pres_entity(void) const; + string get_tuple_id(void) const; + string get_basic_status(void) const; + //@} + + /** @name Setters */ + //@{ + void set_pres_entity(const string &_pres_entity); + void set_tuple_id(const string &_tuple_id); + void set_basic_status(const string &_basic_status);; + //@} + + /** + * Parse a text representation of the body. + * If parsing succeeds, then the state is extracted. + * @param s [in] Text to parse. + * @return True if parsing and state extracting succeeded, false otherwise. + */ + virtual bool parse(const string &s); +}; + +#endif diff --git a/src/presence/presence_dialog.cpp b/src/presence/presence_dialog.cpp new file mode 100644 index 0000000..f281493 --- /dev/null +++ b/src/presence/presence_dialog.cpp @@ -0,0 +1,35 @@ +/* + Copyright (C) 2005-2009 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 "presence_dialog.h" + +#include "presence_subscription.h" +#include "phone_user.h" +#include "audits/memman.h" + +t_presence_dialog::t_presence_dialog(t_phone_user *_phone_user, t_presence_state *presence_state) : + t_subscription_dialog(_phone_user) +{ + subscription = new t_presence_subscription(this, presence_state); + MEMMAN_NEW(subscription); +} + +t_presence_dialog *t_presence_dialog::copy(void) { + // Copy is not needed. + assert(false); +} diff --git a/src/presence/presence_dialog.h b/src/presence/presence_dialog.h new file mode 100644 index 0000000..10513bd --- /dev/null +++ b/src/presence/presence_dialog.h @@ -0,0 +1,47 @@ +/* + Copyright (C) 2005-2009 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 +*/ + +/** + * @file + * Dialog for presence subscription (RFC 3856) + */ + +#ifndef _PRESENCE_DIALOG_H +#define _PRESENCE_DIALOG_H + +#include "subscription_dialog.h" +#include "presence_state.h" + +// Forward declaration +class t_phone_user; + +/** Dialog for presence subscription (RFC 3856) */ +class t_presence_dialog : public t_subscription_dialog { +public: + /** + * Constructor. + * @param _phone_user [in] Phone user owning the dialog. + * @param presence_state [in] Presence state that is updated by notification + * on this dialog + */ + t_presence_dialog(t_phone_user *_phone_user, t_presence_state *presence_state); + + virtual t_presence_dialog *copy(void); +}; + +#endif diff --git a/src/presence/presence_epa.cpp b/src/presence/presence_epa.cpp new file mode 100644 index 0000000..0d4071f --- /dev/null +++ b/src/presence/presence_epa.cpp @@ -0,0 +1,71 @@ +/* + Copyright (C) 2005-2009 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 "presence_epa.h" + +#include "pidf_body.h" +#include "audits/memman.h" +#include "parser/hdr_event.h" + +t_presence_epa::t_presence_epa(t_phone_user *pu) : + t_epa(pu, SIP_EVENT_PRESENCE, t_url(pu->get_user_profile()->create_user_uri(false))), + basic_state(t_presence_state::ST_BASIC_CLOSED), + tuple_id(NEW_PIDF_TUPLE_ID) +{} + +t_presence_state::t_basic_state t_presence_epa::get_basic_state(void) const { + return basic_state; +} + +bool t_presence_epa::recv_response(t_response *r, t_tuid tuid, t_tid tid) { + t_epa::recv_response(r, tuid, tid); + + // Notify observers so they can get the latest publication state. + notify(); + + return true; +} + +void t_presence_epa::publish_presence(t_presence_state::t_basic_state _basic_state) { + if (_basic_state != t_presence_state::ST_BASIC_CLOSED && + _basic_state != t_presence_state::ST_BASIC_OPEN) + { + // Cannot publish internal states. + return; + } + + t_user *user_config = phone_user->get_user_profile(); + basic_state = _basic_state; + + // Create PIDF document + t_pidf_xml_body *pidf = new t_pidf_xml_body(); + MEMMAN_NEW(pidf); + pidf->set_pres_entity(user_config->create_user_uri(false)); + pidf->set_tuple_id(tuple_id); + pidf->set_basic_status(t_presence_state::basic_state2pidf_str(_basic_state)); + + publish(user_config->get_pres_publication_time(), pidf); + + // NOTE: the observers will be notified of the state change, when the + // PUBLISH response is received. +} + +void t_presence_epa::clear(void) { + t_epa::clear(); + basic_state = t_presence_state::ST_BASIC_CLOSED; +} diff --git a/src/presence/presence_epa.h b/src/presence/presence_epa.h new file mode 100644 index 0000000..f224426 --- /dev/null +++ b/src/presence/presence_epa.h @@ -0,0 +1,67 @@ +/* + Copyright (C) 2005-2009 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 +*/ + +/** + * @file + * Presence Event Publication Agent (EPA) [RFC 3903] + */ + +#ifndef _PRESENCE_EPA_H +#define _PRESENCE_EPA_H + +#include <string> + +#include "epa.h" +#include "presence_state.h" +#include "patterns/observer.h" + +using namespace std; + +/** Presence Event Publication Agent (EPA) [RFC 3903] */ +class t_presence_epa : public t_epa, public patterns::t_subject { +private: + /** Basic presence state. */ + t_presence_state::t_basic_state basic_state; + + /** Tuple id to be put in the PIDF documents. */ + string tuple_id; + +public: + /** Constructor */ + t_presence_epa(t_phone_user *pu); + + /** @name Getters */ + //@{ + t_presence_state::t_basic_state get_basic_state(void) const; + //@} + + virtual bool recv_response(t_response *r, t_tuid tuid, t_tid tid); + + /** + * Publish presence state. + * @param _basic_state [in] The basic presence state. + * @pre _basic_state must be one of the following values: + * - @ref ST_BASIC_CLOSED + * - @ref ST_BASIC_OPEN + */ + void publish_presence(t_presence_state::t_basic_state _basic_state); + + virtual void clear(void); +}; + +#endif diff --git a/src/presence/presence_state.cpp b/src/presence/presence_state.cpp new file mode 100644 index 0000000..91c9ba8 --- /dev/null +++ b/src/presence/presence_state.cpp @@ -0,0 +1,100 @@ +/* + Copyright (C) 2005-2009 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 "presence_state.h" + +#include <cassert> +#include "buddy.h" +#include "pidf_body.h" +#include "log.h" + +string t_presence_state::basic_state2str(t_presence_state::t_basic_state state) { + switch (state) { + case ST_BASIC_UNKNOWN: + return "unknown"; + case ST_BASIC_CLOSED: + return "closed"; + case ST_BASIC_OPEN: + return "open"; + case ST_BASIC_FAILED: + return "failed"; + case ST_BASIC_REJECTED: + return "rejeceted"; + default: + return "UNKNOWN"; + } +} + +string t_presence_state::basic_state2pidf_str(t_presence_state::t_basic_state state) { + if (state == ST_BASIC_OPEN) { + return PIDF_STATUS_BASIC_OPEN; + } + + // Convert all other states to "closed". + return PIDF_STATUS_BASIC_CLOSED; +} + +t_presence_state::t_presence_state() { + assert(false); +} + +t_presence_state::t_presence_state(t_buddy *_buddy) : + buddy(_buddy), + basic_state(ST_BASIC_UNKNOWN) +{ +} + +t_presence_state::t_basic_state t_presence_state::get_basic_state(void) const { + t_basic_state result; + mtx_state.lock(); + result = basic_state; + mtx_state.unlock(); + return result; +} + +string t_presence_state::get_failure_msg(void) const { + string result; + mtx_state.lock(); + result = failure_msg; + mtx_state.unlock(); + return result; +} + +void t_presence_state::set_basic_state(t_presence_state::t_basic_state state) { + mtx_state.lock(); + basic_state = state; + + log_file->write_header("t_presence_state::set_basic_state", LOG_NORMAL, LOG_DEBUG); + log_file->write_raw("Presence state changed to: "); + log_file->write_raw(basic_state2str(basic_state)); + log_file->write_endl(); + log_file->write_raw(buddy->get_sip_address()); + log_file->write_endl(); + log_file->write_footer(); + + mtx_state.unlock(); + + // Notify the stat change to all observers of the buddy. + buddy->notify(); +} + +void t_presence_state::set_failure_msg(const string &msg) { + mtx_state.lock(); + failure_msg = msg; + mtx_state.unlock(); +} diff --git a/src/presence/presence_state.h b/src/presence/presence_state.h new file mode 100644 index 0000000..f86082e --- /dev/null +++ b/src/presence/presence_state.h @@ -0,0 +1,94 @@ +/* + Copyright (C) 2005-2009 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 +*/ + +/** + * @file + * Presence state (RFC 3863) + */ + +#ifndef _PRESENCE_STATE_H +#define _PRESENCE_STATE_H + +#include <string> +#include "threads/mutex.h" + +using namespace std; + +// Forward declaration +class t_buddy; + +/** Presence state */ +class t_presence_state { +public: + /** Basic state (RFC 3863 4.1.4) */ + enum t_basic_state { + ST_BASIC_UNKNOWN, /**< Presence state is unknown. */ + ST_BASIC_CLOSED, /**< Unable to accept communication. */ + ST_BASIC_OPEN, /**< Ready to accept communication. */ + ST_BASIC_FAILED, /**< Failed to determine basic state. */ + ST_BASIC_REJECTED,/**< Subscription has been rejected. */ + }; + + /** + * Convert a basic state to a string representation for internal usage. + * @param state [in] A basic state value. + * @return String representation of the basic state. + */ + static string basic_state2str(t_basic_state state); + + /** + * Convert a basic state to a PIDF string representation. + * @param state [in] A basic state value. + * @return PIDF representation of the basic state. + */ + static string basic_state2pidf_str(t_basic_state state); + +private: + /** Mutex for concurrent access to the presence state. */ + mutable t_mutex mtx_state; + + /** Buddy owning this state. */ + t_buddy *buddy; + + /** Basic presence state. */ + t_basic_state basic_state; + + /** Detailed failure message */ + string failure_msg; + + /** Protect the default constructor from being used. */ + t_presence_state(); + +public: + /** Constructor. */ + t_presence_state(t_buddy *_buddy); + + /** @name Getters */ + //@{ + t_basic_state get_basic_state(void) const; + string get_failure_msg(void) const; + //@} + + /** @name Setters */ + //@{ + void set_basic_state(t_basic_state state); + void set_failure_msg(const string &msg); + //@} +}; + +#endif diff --git a/src/presence/presence_subscription.cpp b/src/presence/presence_subscription.cpp new file mode 100644 index 0000000..af1514e --- /dev/null +++ b/src/presence/presence_subscription.cpp @@ -0,0 +1,144 @@ +/* + Copyright (C) 2005-2009 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 "presence_subscription.h" + +#include <cassert> + +#include "pidf_body.h" + +#include "log.h" +#include "util.h" +#include "parser/hdr_event.h" +#include "audits/memman.h" + +t_request *t_presence_subscription::create_subscribe(unsigned long expires) const { + t_request *r = t_subscription::create_subscribe(expires); + SET_PRESENCE_HDR_ACCEPT(r->hdr_accept); + + return r; +} + +t_presence_subscription::t_presence_subscription(t_presence_dialog *_dialog, t_presence_state *_state) : + t_subscription(_dialog, SR_SUBSCRIBER, SIP_EVENT_PRESENCE), + presence_state(_state) +{ +} + +bool t_presence_subscription::recv_notify(t_request *r, t_tuid tuid, t_tid tid) { + if (t_subscription::recv_notify(r, tuid, tid)) return true; + + bool unsupported_body = false; + + // NOTE: if the subscription is still pending (RFC 3265 3.2.4), then the + // information in the body has no meaning. + // NOTE: a NOTIFY request may have no body (RFC 3856 6.6.2) + if (r->body && r->body->get_type() == BODY_PIDF_XML && + !is_pending()) + { + t_pidf_xml_body *body = dynamic_cast<t_pidf_xml_body *>(r->body); + assert(body); + + string basic = body->get_basic_status(); + if (basic == PIDF_STATUS_BASIC_OPEN) { + presence_state->set_basic_state(t_presence_state::ST_BASIC_OPEN); + } else if (basic == PIDF_STATUS_BASIC_CLOSED) { + presence_state->set_basic_state(t_presence_state::ST_BASIC_CLOSED); + } else { + log_file->write_header("t_presence_subscription::recv_notify", + LOG_NORMAL, LOG_WARNING); + log_file->write_raw("Unknown basic status in pidf: "); + log_file->write_raw(basic); + log_file->write_endl(); + log_file->write_footer(); + + presence_state->set_basic_state(t_presence_state::ST_BASIC_UNKNOWN); + } + } + + // Verify if there is an usupported body. + if (r->body && r->body->get_type() != BODY_PIDF_XML) { + unsupported_body = true; + } + + if (state == SS_TERMINATED) { + if (may_resubscribe) { + presence_state->set_basic_state(t_presence_state::ST_BASIC_UNKNOWN); + log_file->write_report("Presence subscription terminated.", + "t_presence_subscription::recv_notify"); + } else { + if (reason_termination == EV_REASON_REJECTED) { + presence_state->set_basic_state(t_presence_state::ST_BASIC_REJECTED); + + log_file->write_report("Presence agent rejected the subscription.", + "t_presence_subscription::recv_notify"); + } else { + // The PA ended the subscription and indicated + // that resubscription is not possible. So no presence status + // can be retrieved anymore. This should not happen. + // Show it as a failure to the user. + presence_state->set_failure_msg(reason_termination); + presence_state->set_basic_state(t_presence_state::ST_BASIC_FAILED); + + log_file->write_report( + "Presence agent permanently terminated the subscription.", + "t_presence_subscription::recv_notify"); + } + } + } + + t_response *resp; + if (unsupported_body) { + resp = r->create_response(R_415_UNSUPPORTED_MEDIA_TYPE); + SET_PRESENCE_HDR_ACCEPT(r->hdr_accept); + } else { + resp = r->create_response(R_200_OK); + } + send_response(user_config, resp, 0, tid); + MEMMAN_DELETE(resp); + delete resp; + + return true; +} + +bool t_presence_subscription::recv_subscribe_response(t_response *r, t_tuid tuid, t_tid tid) { + // Parent handles the SUBSCRIBE response + (void)t_subscription::recv_subscribe_response(r, tuid, tid); + + // If the subscription is terminated after the SUBSCRIBE response, it means + // that subscription failed. + if (state == SS_TERMINATED) { + if (r->code == R_403_FORBIDDEN || r->code == R_603_DECLINE) { + presence_state->set_basic_state(t_presence_state::ST_BASIC_REJECTED); + + log_file->write_report("Presence subscription rejected.", + "t_presence_subscription::recv_subscribe_response"); + } else { + string failure = int2str(r->code); + failure += ' '; + failure += r->reason; + presence_state->set_failure_msg(failure); + presence_state->set_basic_state(t_presence_state::ST_BASIC_FAILED); + + log_file->write_report("Presence subscription failed.", + "t_presence_subscription::recv_subscribe_response"); + } + } + + return true; +} diff --git a/src/presence/presence_subscription.h b/src/presence/presence_subscription.h new file mode 100644 index 0000000..09fb6ba --- /dev/null +++ b/src/presence/presence_subscription.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2005-2009 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 +*/ + +/** + * @file + * Presence subscription (RFC 3856) + */ + +#ifndef _PRESENCE_SUBSCRIPTION_H +#define _PRESENCE_SUBSCRIPTION_H + +#include "presence_state.h" +#include "presence_dialog.h" +#include "subscription.h" + +/** Subscription to the presence event (RFC 3856) */ +class t_presence_subscription : public t_subscription { +private: + t_presence_state *presence_state; + +protected: + virtual t_request *create_subscribe(unsigned long expires) const; + +public: + /** + * Constructor. + * @param _dialog [in] Dialog for the presence subscription. + * @param _state [in] Current presence state. + */ + t_presence_subscription(t_presence_dialog *_dialog, t_presence_state *_state); + + virtual bool recv_notify(t_request *r, t_tuid tuid, t_tid tid); + virtual bool recv_subscribe_response(t_response *r, t_tuid tuid, t_tid tid); +}; + +#endif |