Commit 77b017ca authored by Tristan Matthews's avatar Tristan Matthews

* #29736: presence: merge sippublish into sippresence

parent f03f5101
...@@ -20,9 +20,8 @@ libsiplink_la_SOURCES = \ ...@@ -20,9 +20,8 @@ libsiplink_la_SOURCES = \
pres_sub_server.cpp\ pres_sub_server.cpp\
pres_sub_server.h\ pres_sub_server.h\
pres_sub_client.cpp\ pres_sub_client.cpp\
pres_sub_client.h\ pres_sub_client.h
sippublish.cpp\
sippublish.h
if BUILD_SDES if BUILD_SDES
libsiplink_la_SOURCES+= sdes_negotiator.cpp \ libsiplink_la_SOURCES+= sdes_negotiator.cpp \
sdes_negotiator.h \ sdes_negotiator.h \
......
...@@ -30,14 +30,14 @@ ...@@ -30,14 +30,14 @@
*/ */
#include "sippresence.h"
#include "logger.h" #include "logger.h"
#include "manager.h" #include "manager.h"
#include "client/client.h" #include "client/client.h"
#include "client/callmanager.h" #include "client/callmanager.h"
#include "client/presencemanager.h" #include "client/presencemanager.h"
#include "sipaccount.h" #include "sipaccount.h"
#include "sippublish.h" #include "sip_utils.h"
#include "sippresence.h"
#include "pres_sub_server.h" #include "pres_sub_server.h"
#include "pres_sub_client.h" #include "pres_sub_client.h"
#include "sipvoiplink.h" #include "sipvoiplink.h"
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
#define MAX_N_PRES_SUB_CLIENT 20 #define MAX_N_PRES_SUB_CLIENT 20
SIPPresence::SIPPresence(SIPAccount *acc) SIPPresence::SIPPresence(SIPAccount *acc)
: publish_sess() : publish_sess_()
, pres_status_data_() , pres_status_data_()
, enabled_(true) , enabled_(true)
, acc_(acc) , acc_(acc)
...@@ -74,10 +74,10 @@ SIPPresence::SIPPresence(SIPAccount *acc) ...@@ -74,10 +74,10 @@ SIPPresence::SIPPresence(SIPAccount *acc)
SIPPresence::~SIPPresence() SIPPresence::~SIPPresence()
{ {
/* Flush the lists */ /* Flush the lists */
for (const auto &c : pres_sub_client_list_) for (const auto & c : pres_sub_client_list_)
removePresSubClient(c) ; removePresSubClient(c) ;
for (const auto &s : pres_sub_server_list_) for (const auto & s : pres_sub_server_list_)
removePresSubServer(s); removePresSubServer(s);
} }
...@@ -160,13 +160,14 @@ void SIPPresence::reportPresSubClientNotification(const std::string& uri, pjsip_ ...@@ -160,13 +160,14 @@ void SIPPresence::reportPresSubClientNotification(const std::string& uri, pjsip_
void SIPPresence::subscribeClient(const std::string& uri, bool flag) void SIPPresence::subscribeClient(const std::string& uri, bool flag)
{ {
/* Check if the buddy was already subscribed */ /* Check if the buddy was already subscribed */
for (const auto &c : pres_sub_client_list_) for (const auto & c : pres_sub_client_list_) {
if (c->getURI() == uri) { if (c->getURI() == uri) {
DEBUG("-PresSubClient:%s exists in the list. Replace it.", uri.c_str()); DEBUG("-PresSubClient:%s exists in the list. Replace it.", uri.c_str());
delete c; delete c;
removePresSubClient(c); removePresSubClient(c);
break; break;
} }
}
if (pres_sub_client_list_.size() >= MAX_N_PRES_SUB_CLIENT) { if (pres_sub_client_list_.size() >= MAX_N_PRES_SUB_CLIENT) {
WARN("Can't add PresSubClient, max number reached."); WARN("Can't add PresSubClient, max number reached.");
...@@ -210,12 +211,13 @@ void SIPPresence::reportnewServerSubscriptionRequest(PresSubServer *s) ...@@ -210,12 +211,13 @@ void SIPPresence::reportnewServerSubscriptionRequest(PresSubServer *s)
void SIPPresence::approvePresSubServer(const std::string& uri, bool flag) void SIPPresence::approvePresSubServer(const std::string& uri, bool flag)
{ {
for (const auto &s : pres_sub_server_list_) for (const auto & s : pres_sub_server_list_) {
if (s->matches((char *) uri.c_str())) { if (s->matches((char *) uri.c_str())) {
DEBUG("Approve Presence_subscription_server for %s: %s.", s->remote, flag ? "true" : "false"); DEBUG("Approve Presence_subscription_server for %s: %s.", s->remote, flag ? "true" : "false");
s->approve(flag); s->approve(flag);
// return; // 'return' would prevent multiple-time subscribers from spam // return; // 'return' would prevent multiple-time subscribers from spam
} }
}
} }
...@@ -240,7 +242,7 @@ void SIPPresence::notifyPresSubServer() ...@@ -240,7 +242,7 @@ void SIPPresence::notifyPresSubServer()
{ {
DEBUG("Iterating through Presence_subscription_server:"); DEBUG("Iterating through Presence_subscription_server:");
for (const auto &s : pres_sub_server_list_) for (const auto & s : pres_sub_server_list_)
s->notify(); s->notify();
} }
...@@ -292,3 +294,190 @@ void SIPPresence::fillDoc(pjsip_tx_data *tdata, const pres_msg_data *msg_data) ...@@ -292,3 +294,190 @@ void SIPPresence::fillDoc(pjsip_tx_data *tdata, const pres_msg_data *msg_data)
tdata->msg->body = body; tdata->msg->body = body;
} }
} }
static const pjsip_publishc_opt my_publish_opt = {true}; // this is queue_request
/*
* Client presence publication callback.
*/
void
SIPPresence::pres_publish_cb(struct pjsip_publishc_cbparam *param)
{
SIPPresence *pres = (SIPPresence*) param->token;
if (param->code / 100 != 2 || param->status != PJ_SUCCESS) {
pjsip_publishc_destroy(param->pubc);
pres->publish_sess_ = NULL;
if (param->status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(param->status, errmsg, sizeof(errmsg));
ERROR("Client publication (PUBLISH) failed, status=%d, msg=%s", param->status, errmsg);
} else if (param->code == 412) {
/* 412 (Conditional Request Failed)
* The PUBLISH refresh has failed, retry with new one.
*/
WARN("Publish retry.");
pres_publish(pres);
} else {
ERROR("Client publication (PUBLISH) failed (%u/%.*s)",
param->code, param->reason.slen, param->reason.ptr);
}
} else {
if (param->expiration < 1) {
/* Could happen if server "forgot" to include Expires header
* in the response. We will not renew, so destroy the pubc.
*/
pjsip_publishc_destroy(param->pubc);
pres->publish_sess_ = NULL;
}
}
}
/*
* Send PUBLISH request.
*/
pj_status_t
SIPPresence::pres_send_publish(SIPPresence * pres, pj_bool_t active)
{
pjsip_tx_data *tdata;
pj_status_t status;
DEBUG("Send presence %sPUBLISH..", (active ? "" : "un-"));
SIPAccount * acc = pres->getAccount();
std::string contactWithAngles = acc->getFromUri();
contactWithAngles.erase(contactWithAngles.find('>'));
int semicolon = contactWithAngles.find_first_of(":");
std::string contactWithoutAngles = contactWithAngles.substr(semicolon + 1);
// pj_str_t contact = pj_str(strdup(contactWithoutAngles.c_str()));
// pj_memcpy(&pres_status_data.info[0].contact, &contt, sizeof(pj_str_t));;
/* Create PUBLISH request */
if (active) {
char *bpos;
pj_str_t entity;
status = pjsip_publishc_publish(pres->publish_sess_, PJ_TRUE, &tdata);
if (status != PJ_SUCCESS) {
ERROR("Error creating PUBLISH request", status);
goto on_error;
}
pj_str_t from = pj_str(strdup(acc->getFromUri().c_str()));
if ((bpos = pj_strchr(&from, '<')) != NULL) {
char *epos = pj_strchr(&from, '>');
if (epos - bpos < 2) {
pj_assert(!"Unexpected invalid URI");
status = PJSIP_EINVALIDURI;
goto on_error;
}
entity.ptr = bpos + 1;
entity.slen = epos - bpos - 1;
} else {
entity = from;
}
/* Create and add PIDF message body */
status = pjsip_pres_create_pidf(tdata->pool, pres->getStatus(),
&entity, &tdata->msg->body);
if (status != PJ_SUCCESS) {
ERROR("Error creating PIDF for PUBLISH request");
pjsip_tx_data_dec_ref(tdata);
goto on_error;
}
} else {
WARN("Unpublish is not implemented.");
}
pres_msg_data msg_data;
pj_bzero(&msg_data, sizeof(msg_data));
pj_list_init(&msg_data.hdr_list);
pjsip_media_type_init(&msg_data.multipart_ctype, NULL, NULL);
pj_list_init(&msg_data.multipart_parts);
pres->fillDoc(tdata, &msg_data);
/* Send the PUBLISH request */
status = pjsip_publishc_send(pres->publish_sess_, tdata);
if (status == PJ_EPENDING) {
WARN("Previous request is in progress, ");
} else if (status != PJ_SUCCESS) {
ERROR("Error sending PUBLISH request");
goto on_error;
}
return PJ_SUCCESS;
on_error:
if (pres->publish_sess_) {
pjsip_publishc_destroy(pres->publish_sess_);
pres->publish_sess_ = NULL;
}
return status;
}
/* Create client publish session */
pj_status_t
SIPPresence::pres_publish(SIPPresence *pres)
{
pj_status_t status;
const pj_str_t STR_PRESENCE = pj_str("presence");
SIPAccount * acc = pres->getAccount();
pjsip_endpoint *endpt = ((SIPVoIPLink*) acc->getVoIPLink())->getEndpoint();
/* Create and init client publication session */
/* Create client publication */
status = pjsip_publishc_create(endpt, &my_publish_opt,
pres, &pres_publish_cb,
&pres->publish_sess_);
if (status != PJ_SUCCESS) {
pres->publish_sess_ = NULL;
ERROR("Failed to create a publish seesion.");
return status;
}
/* Initialize client publication */
pj_str_t from = pj_str(strdup(acc->getFromUri().c_str()));
status = pjsip_publishc_init(pres->publish_sess_, &STR_PRESENCE, &from, &from, &from, 0xFFFF);
if (status != PJ_SUCCESS) {
ERROR("Failed to init a publish session");
pres->publish_sess_ = NULL;
return status;
}
/* Add credential for authentication */
if (acc->hasCredentials() and pjsip_publishc_set_credentials(pres->publish_sess_, acc->getCredentialCount(), acc->getCredInfo()) != PJ_SUCCESS) {
ERROR("Could not initialize credentials for invite session authentication");
return status;
}
/* Set route-set */
if (acc->hasServiceRoute())
pjsip_regc_set_route_set(acc->getRegistrationInfo(), sip_utils::createRouteSet(acc->getServiceRoute(), pres->getPool()));
/* Send initial PUBLISH request */
status = pres_send_publish(pres, PJ_TRUE);
if (status != PJ_SUCCESS)
return status;
return PJ_SUCCESS;
}
...@@ -44,26 +44,25 @@ ...@@ -44,26 +44,25 @@
#include "pjsip-simple/rpid.h" #include "pjsip-simple/rpid.h"
#include <pj/pool.h> #include <pj/pool.h>
struct pres_msg_data struct pres_msg_data {
{
/** /**
* Additional message headers as linked list. Application can add * Additional message headers as linked list. Application can add
* headers to the list by creating the header, either from the heap/pool * headers to the list by creating the header, either from the heap/pool
* or from temporary local variable, and add the header using * or from temporary local variable, and add the header using
* linked list operation. See pjsip_apps.c for some sample codes. * linked list operation. See pjsip_apps.c for some sample codes.
*/ */
pjsip_hdr hdr_list; pjsip_hdr hdr_list;
/** /**
* MIME type of optional message body. * MIME type of optional message body.
*/ */
pj_str_t content_type; pj_str_t content_type;
/** /**
* Optional message body to be added to the message, only when the * Optional message body to be added to the message, only when the
* message doesn't have a body. * message doesn't have a body.
*/ */
pj_str_t msg_body; pj_str_t msg_body;
/** /**
* Content type of the multipart body. If application wants to send * Content type of the multipart body. If application wants to send
...@@ -102,133 +101,136 @@ struct pj_caching_pool; ...@@ -102,133 +101,136 @@ struct pj_caching_pool;
class SIPPresence { class SIPPresence {
public: public:
/** /**
* Constructor * Constructor
* @param acc the associated sipaccount * @param acc the associated sipaccount
*/ */
SIPPresence(SIPAccount * acc); SIPPresence(SIPAccount * acc);
/** /**
* Destructor * Destructor
*/ */
~SIPPresence(); ~SIPPresence();
/** /**
* Return associated sipaccount * Return associated sipaccount
*/ */
SIPAccount * getAccount() const; SIPAccount * getAccount() const;
/** /**
* Return presence data. * Return presence data.
*/ */
pjsip_pres_status * getStatus(); pjsip_pres_status * getStatus();
/** /**
* Return presence module ID which is actually the same as the VOIP link * Return presence module ID which is actually the same as the VOIP link
*/ */
int getModId() const; int getModId() const;
/** /**
* Return a pool for generic functions. * Return a pool for generic functions.
*/ */
pj_pool_t* getPool() const; pj_pool_t* getPool() const;
/** /**
* Activate the module (PUBLISH/SUBSCRIBE) * Activate the module (PUBLISH/SUBSCRIBE)
*/ */
void enable(bool flag); void enable(bool flag);
/** /**
* Fill xml document, the header and the body * Fill xml document, the header and the body
*/ */
void fillDoc(pjsip_tx_data *tdata, const pres_msg_data *msg_data); void fillDoc(pjsip_tx_data *tdata, const pres_msg_data *msg_data);
/** /**
* Modify the presence data * Modify the presence data
* @param status is basically "open" or "close" * @param status is basically "open" or "close"
*/ */
void updateStatus(bool status, const std::string &note); void updateStatus(bool status, const std::string &note);
/** /**
* Send the presence data in a PUBLISH to the PBX or in a NOTIFY * Send the presence data in a PUBLISH to the PBX or in a NOTIFY
* to a remote subscriber (IP2IP) * to a remote subscriber (IP2IP)
*/ */
void sendPresence(bool status, const std::string &note); void sendPresence(bool status, const std::string &note);
/** /**
* Send a signal to the client on DBus. The signal contain the status * Send a signal to the client on DBus. The signal contain the status
* of a remote user. * of a remote user.
*/ */
void reportPresSubClientNotification(const std::string& uri, pjsip_pres_status * status); void reportPresSubClientNotification(const std::string& uri, pjsip_pres_status * status);
/** /**
* Send a SUBSCRIBE request to PBX/IP2IP * Send a SUBSCRIBE request to PBX/IP2IP
* @param buddyUri Remote user that we want to subscribe * @param buddyUri Remote user that we want to subscribe
*/ */
void subscribeClient(const std::string& uri, bool flag); void subscribeClient(const std::string& uri, bool flag);
/** /**
* Add a buddy in the buddy list. * Add a buddy in the buddy list.
* @param b PresSubClient pointer * @param b PresSubClient pointer
*/ */
void addPresSubClient(PresSubClient *b); void addPresSubClient(PresSubClient *b);
/** /**
* Remove a buddy from the list. * Remove a buddy from the list.
* @param b PresSubClient pointer * @param b PresSubClient pointer
*/ */
void removePresSubClient(PresSubClient *b); void removePresSubClient(PresSubClient *b);
/** /**
* IP2IP context. * IP2IP context.
* Report new Subscription to the client, waiting for approval. * Report new Subscription to the client, waiting for approval.
* @param s PresenceSubcription pointer. * @param s PresenceSubcription pointer.
*/ */
void reportnewServerSubscriptionRequest(PresSubServer *s); void reportnewServerSubscriptionRequest(PresSubServer *s);
/** /**
* IP2IP context. * IP2IP context.
* Process new subscription based on client decision. * Process new subscription based on client decision.
* @param flag client decision. * @param flag client decision.
* @param uri uri of the remote subscriber * @param uri uri of the remote subscriber
*/ */
void approvePresSubServer(const std::string& uri, bool flag); void approvePresSubServer(const std::string& uri, bool flag);
/** /**
* IP2IP context. * IP2IP context.
* Add a server associated to a subscriber in the list. * Add a server associated to a subscriber in the list.
* @param s PresenceSubcription pointer. * @param s PresenceSubcription pointer.
*/ */
void addPresSubServer(PresSubServer *s); void addPresSubServer(PresSubServer *s);
/** /**
* IP2IP context. * IP2IP context.
* Remove a server associated to a subscriber from the list. * Remove a server associated to a subscriber from the list.
* @param s PresenceSubcription pointer. * @param s PresenceSubcription pointer.
*/ */
void removePresSubServer(PresSubServer *s); void removePresSubServer(PresSubServer *s);
/** /**
* IP2IP context. * IP2IP context.
* Iterate through the subscriber list and send NOTIFY to each. * Iterate through the subscriber list and send NOTIFY to each.
*/ */
void notifyPresSubServer(); void notifyPresSubServer();
bool isEnabled() const { bool isEnabled() const {
return enabled_; return enabled_;
} }
const std::list< PresSubClient *> getClientSubscriptions() { const std::list< PresSubClient *> getClientSubscriptions() {
return pres_sub_client_list_; return pres_sub_client_list_;
} }
void lock(); void lock();
void unlock(); void unlock();
pjsip_publishc *publish_sess; /**< Client publication session.*/ private:
private: NON_COPYABLE(SIPPresence);
pjsip_pres_status pres_status_data_; /**< Presence Data.*/
static pj_status_t pres_publish(SIPPresence *pres);
pj_bool_t enabled_; /**< Allow for status publish,*/ static void pres_publish_cb(struct pjsip_publishc_cbparam *param);
NON_COPYABLE(SIPPresence); static pj_status_t pres_send_publish(SIPPresence *pres, pj_bool_t active);
SIPAccount * acc_; /**< Associated SIP account. */ pjsip_publishc *publish_sess_; /**< Client publication session.*/
std::list< PresSubServer *> pres_sub_server_list_; /**< Subscribers list.*/ pjsip_pres_status pres_status_data_; /**< Presence Data.*/
std::list< PresSubClient *> pres_sub_client_list_; /**< Subcribed buddy list.*/
pj_bool_t enabled_; /**< Allow for status publish,*/
pj_mutex_t *mutex_; /**< Mutex protection for this data */
unsigned mutex_nesting_level_; /**< Mutex nesting level. */ SIPAccount * acc_; /**< Associated SIP account. */
pj_thread_t *mutex_owner_; /**< Mutex owner. */ std::list< PresSubServer *> pres_sub_server_list_; /**< Subscribers list.*/
pj_caching_pool cp_; /**< Global pool factory. */ std::list< PresSubClient *> pres_sub_client_list_; /**< Subcribed buddy list.*/
pj_pool_t *pool_; /**< pjsua's private pool. */
pj_mutex_t *mutex_;
unsigned mutex_nesting_level_;
pj_thread_t *mutex_owner_;
pj_caching_pool cp_;
pj_pool_t *pool_;
}; };
#endif #endif
/*
* Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
*
* Author: Patrick Keroulas <patrick.keroulas@savoirfairelinux.com>
*