Skip to content
Snippets Groups Projects
Commit 13b30305 authored by Patrick Keroulas's avatar Patrick Keroulas
Browse files

* #28472 : Add missing files.

parent 093d8afe
No related branches found
No related tags found
No related merge requests found
/*
* Copyright (C) 2012, 2013 LOTES TM LLC
* Author : Andrey Loukhnov <aol.nnov@gmail.com>
*
* This file is a part of pult5-voip
*
* pult5-voip 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 3 of the License, or
* (at your option) any later version.
*
* pult5-voip 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 programm. If not, see <http://www.gnu.org/licenses/>.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify pult5-voip, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, LOTES-TM LLC
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include <pj/log.h>
#include <pj/rand.h>
#include <pjsip/sip_module.h>
#include <pjsip/sip_types.h>
#include <pjsip/sip_event.h>
#include <pjsip/sip_transaction.h>
#include <pjsip/sip_dialog.h>
#include <pjsip/sip_endpoint.h>
#include <string>
#include <pj/pool.h>
#include <pjsip/sip_ua_layer.h>
#include <pjsip-simple/evsub.h>
#include <unistd.h>
#include "pres_sub_client.h"
#include "sipaccount.h"
#include "sippresence.h"
#include "sipvoiplink.h"
#include "manager.h"
#include "logger.h"
#define BUDDY_SUB_TERM_REASON_LEN 32
#define PRES_TIMER 300
int modId;
void buddy_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry) {
(void) th;
PresSubClient *b = (PresSubClient *) entry->user_data;
b->reportPresence();
}
/* Callback called when *client* subscription state has changed. */
void buddy_evsub_on_state(pjsip_evsub *sub, pjsip_event *event) {
PresSubClient *buddy;
PJ_UNUSED_ARG(event);
/* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
* a dialog attached to it, lock_buddy() will use the dialog
* lock, which we are currently holding!
*/
buddy = (PresSubClient *) pjsip_evsub_get_mod_data(sub, modId);
if (buddy) {
buddy->incLock();
DEBUG("Presence subscription to '%s' is '%s'", buddy->getURI().c_str(),
pjsip_evsub_get_state_name(sub) ? pjsip_evsub_get_state_name(sub) : "null");
pjsip_evsub_state state = pjsip_evsub_get_state(sub);
if(state == PJSIP_EVSUB_STATE_ACCEPTED){
DEBUG("PresSubClient accepted.");
buddy->accept();
}
else if (state == PJSIP_EVSUB_STATE_TERMINATED) {
int resub_delay = -1;
// const pj_str_t *pjTermReason = pjsip_evsub_get_termination_reason(sub);
// std::string termReason(pjTermReason->ptr,
// pjTermReason->slen > BUDDY_SUB_TERM_REASON_LEN?
// BUDDY_SUB_TERM_REASON_LEN:
// pjTermReason->slen
// );
pj_strdup_with_null(buddy->pool, &buddy->term_reason, pjsip_evsub_get_termination_reason(sub));
// buddy->setTermReason(termReason);
// buddy->setTermCode(200);
buddy->term_code = 200;
/* Determine whether to resubscribe automatically */
if (event && event->type == PJSIP_EVENT_TSX_STATE) {
const pjsip_transaction *tsx = event->body.tsx_state.tsx;
if (pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0) {
// buddy->setTermCode(tsx->status_code);
buddy->term_code = tsx->status_code;
switch (tsx->status_code) {
case PJSIP_SC_CALL_TSX_DOES_NOT_EXIST:
/* 481: we refreshed too late? resubscribe
* immediately.
*/
/* But this must only happen when the 481 is received
* on subscription refresh request. We MUST NOT try to
* resubscribe automatically if the 481 is received
* on the initial SUBSCRIBE (if server returns this
* response for some reason).
*/
if (buddy->dlg->remote.contact)
resub_delay = 500;
break;
}
} else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
if (buddy->isTermReason("deactivated") || buddy->isTermReason("timeout")) {
/* deactivated: The subscription has been terminated,
* but the subscriber SHOULD retry immediately with
* a new subscription.
*/
/* timeout: The subscription has been terminated
* because it was not refreshed before it expired.
* Clients MAY re-subscribe immediately. The
* "retry-after" parameter has no semantics for
* "timeout".
*/
resub_delay = 500;
} else if (buddy->isTermReason("probation") || buddy->isTermReason("giveup")) {
/* probation: The subscription has been terminated,
* but the client SHOULD retry at some later time.
* If a "retry-after" parameter is also present, the
* client SHOULD wait at least the number of seconds
* specified by that parameter before attempting to re-
* subscribe.
*/
/* giveup: The subscription has been terminated because
* the notifier could not obtain authorization in a
* timely fashion. If a "retry-after" parameter is
* also present, the client SHOULD wait at least the
* number of seconds specified by that parameter before
* attempting to re-subscribe; otherwise, the client
* MAY retry immediately, but will likely get put back
* into pending state.
*/
const pjsip_sub_state_hdr *sub_hdr;
pj_str_t sub_state = {"Subscription-State", 18 };
const pjsip_msg *msg;
msg = event->body.tsx_state.src.rdata->msg_info.msg;
sub_hdr = (const pjsip_sub_state_hdr*) pjsip_msg_find_hdr_by_name(msg, &sub_state, NULL);
if (sub_hdr && sub_hdr->retry_after > 0)
resub_delay = sub_hdr->retry_after * 1000;
}
}
}
/* For other cases of subscription termination, if resubscribe
* timer is not set, schedule with default expiration (plus minus
* some random value, to avoid sending SUBSCRIBEs all at once)
*/
if (resub_delay == -1) {
// pj_assert(PRES_TIMER >= 3);
resub_delay = PRES_TIMER * 1000;// - 2500 + (pj_rand() % 5000);
}
buddy->sub = sub;
buddy->rescheduleTimer(PJ_TRUE, resub_delay);
}/* else {
This will clear the last termination code/reason
buddy->term_code = 0;
buddy->term_reason.ptr = NULL;
}*/
/* Clear subscription */
/* if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
pjsip_evsub_terminate(buddy->sub, PJ_FALSE); // = NULL;
buddy->status.info_cnt = 0;
buddy->dlg = NULL;
buddy->rescheduleTimer(PJ_FALSE, 0);
// pjsip_evsub_set_mod_data(sub, modId, NULL);
}*/
// pj_log_pop_indent();
buddy->decLock();
}
}
/* Callback when transaction state has changed. */
void buddy_evsub_on_tsx_state(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) {
PresSubClient *buddy;
pjsip_contact_hdr *contact_hdr;
/* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
* a dialog attached to it, lock_buddy() will use the dialog
* lock, which we are currently holding!
*/
buddy = (PresSubClient *) pjsip_evsub_get_mod_data(sub, modId);
if (!buddy) {
return;
}
buddy->incLock();
/* We only use this to update buddy's Contact, when it's not
* set.
*/
if (buddy->contact.slen != 0) {
/* Contact already set */
buddy->decLock();
return;
}
/* Only care about 2xx response to outgoing SUBSCRIBE */
if (tsx->status_code / 100 != 2 || tsx->role != PJSIP_UAC_ROLE || event->type != PJSIP_EVENT_RX_MSG
|| pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method()) != 0) {
buddy->decLock();
return;
}
/* Find contact header. */
contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg, PJSIP_H_CONTACT,
NULL);
if (!contact_hdr || !contact_hdr->uri) {
buddy->decLock();
return;
}
buddy->contact.ptr = (char*) pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE);
buddy->contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri, buddy->contact.ptr,
PJSIP_MAX_URL_SIZE);
if (buddy->contact.slen < 0)
buddy->contact.slen = 0;
buddy->decLock();
}
/* Callback called when we receive NOTIFY */
static void buddy_evsub_on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text,
pjsip_hdr *res_hdr, pjsip_msg_body **p_body) {
PresSubClient *buddy;
/* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
* a dialog attached to it, lock_buddy() will use the dialog
* lock, which we are currently holding!
*/
buddy = (PresSubClient *) pjsip_evsub_get_mod_data(sub, modId);
if (!buddy){
ERROR("Couldn't create new buddy");
return;
}
pjsip_pres_get_status(sub, &buddy->status);
buddy->reportPresence();
/* The default is to send 200 response to NOTIFY.
* Just leave it there..
*/
PJ_UNUSED_ARG(rdata);
PJ_UNUSED_ARG(p_st_code);
PJ_UNUSED_ARG(p_st_text);
PJ_UNUSED_ARG(res_hdr);
PJ_UNUSED_ARG(p_body);
buddy->decLock();
}
PresSubClient::PresSubClient(const std::string& uri_, SIPAccount *acc_) :
acc(acc_),
uri(pj_str(strdup(uri_.c_str()))),
contact(pj_str(strdup(acc->getFromUri().c_str()))),
display(),
dlg(NULL),
monitor(false),
name(),
cp_(),
pool(0),
status(),
sub(NULL),
term_code(0),
term_reason(),
timer(),
user_data(NULL),
lock_count(0)
{
pj_caching_pool_init(&cp_, &pj_pool_factory_default_policy, 0);
pool = pj_pool_create(&cp_.factory, "buddy", 512, 512, NULL);
}
PresSubClient::~PresSubClient() {
while(lock_count >0) {
usleep(200);
}
DEBUG("Destroying buddy object with uri %s", uri.ptr);
rescheduleTimer(PJ_FALSE, 0);
unsubscribe();
pj_pool_release(pool);
}
bool PresSubClient::isSubscribed() {
return this->monitor;
}
std::string PresSubClient::getURI() {
std::string buddyURI(uri.ptr, uri.slen);
return buddyURI;
}
bool PresSubClient::isTermReason(std::string reason) {
std::string myReason(term_reason.ptr, term_reason.slen);
return !myReason.compare(reason);
}
void PresSubClient::rescheduleTimer(bool reschedule, unsigned msec) {
if (timer.id) {
// pjsua_cancel_timer(&timer);
pjsip_endpt_cancel_timer(((SIPVoIPLink*) acc->getVoIPLink())->getEndpoint(), &timer);
timer.id = PJ_FALSE;
}
if (reschedule) {
pj_time_val delay;
WARN("Resubscribing buddy %.*s in %u ms (reason: %.*s)",
uri.slen, uri.ptr, msec, (int) term_reason.slen, term_reason.ptr);
monitor = PJ_TRUE;
pj_timer_entry_init(&timer, 0, this, &buddy_timer_cb);
delay.sec = 0;
delay.msec = msec;
pj_time_val_normalize(&delay);
// if (pjsua_schedule_timer(&timer, &delay)==PJ_SUCCESS)
if (pjsip_endpt_schedule_timer(((SIPVoIPLink*) acc->getVoIPLink())->getEndpoint(), &timer, &delay) == PJ_SUCCESS) {
// timer.id = PJ_TRUE;
}
}
}
void PresSubClient::accept() {
acc->getPresence()->addPresSubClient(this);
}
void PresSubClient::reportPresence() {
//incLock();
/* callback*/
acc->getPresence()->reportPresSubClientNotification(getURI(),&status);
//decLock();
}
pj_status_t PresSubClient::updateSubscription() {
if (!monitor) {
/* unsubscribe */
pjsip_tx_data *tdata;
pj_status_t retStatus;
if (sub == NULL) {
WARN("PresSubClient already unsubscribed sub=NULL.");
return PJ_SUCCESS;
}
if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
WARN("PresSubClient already unsubscribed sub=TERMINATED.");
//pjsip_evsub_terminate(sub, PJ_FALSE); //
sub = NULL;
return PJ_SUCCESS;
}
WARN("PresSubClient %s: unsubscribing..", uri.ptr);
retStatus = pjsip_pres_initiate(sub, 0, &tdata);
if (retStatus == PJ_SUCCESS) {
acc->getPresence()->fillDoc(tdata, NULL);
/*if (tdata->msg->type == PJSIP_REQUEST_MSG) {
const pj_str_t STR_USER_AGENT = {"User-Agent", 10};
pj_str_t ua = pj_str(strdup(acc->getUserAgentName().c_str()));
pjsip_hdr *h;
h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool, &STR_USER_AGENT, &ua);
pjsip_msg_add_hdr(tdata->msg, h);
}*/
retStatus = pjsip_pres_send_request(sub, tdata);
}
if (retStatus != PJ_SUCCESS && sub) {
pjsip_pres_terminate(sub, PJ_FALSE);
pjsip_evsub_terminate(sub, PJ_FALSE); // = NULL;
WARN("Unable to unsubscribe presence", status);
}
pjsip_evsub_set_mod_data(sub, modId, NULL); // Not interested with further events
return PJ_SUCCESS;
}
//#if 0
if (sub && dlg) { //do not bother if already subscribed
// return PJ_SUCCESS;
pjsip_evsub_terminate(sub, PJ_FALSE);
DEBUG("Terminate existing sub.");
}
//#endif
//subscribe
pjsip_evsub_user pres_callback;
// pj_pool_t *tmp_pool = NULL; // related to "contact field. TODO: check if this is necessary"
pjsip_tx_data *tdata;
pj_status_t status;
/* Event subscription callback. */
pj_bzero(&pres_callback, sizeof(pres_callback));
pres_callback.on_evsub_state = &buddy_evsub_on_state;
pres_callback.on_tsx_state = &buddy_evsub_on_tsx_state;
pres_callback.on_rx_notify = &buddy_evsub_on_rx_notify;
DEBUG("PresSubClient %s: subscribing presence,using account %s..",
uri.ptr, acc->getAccountID().c_str());
/* Generate suitable Contact header unless one is already set in
* the account
*/
#if 0
if (acc->contact.slen) {
contact = acc->contact;
} else {
tmp_pool = pjsua_pool_create("tmpbuddy", 512, 256);
status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
acc_id, &buddy->uri);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to generate Contact header",
status);
pj_pool_release(tmp_pool);
pj_log_pop_indent();
return;
}
}
#endif
/* Create UAC dialog */
pj_str_t from = pj_str(strdup(acc->getFromUri().c_str()));
status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from, &contact, &uri, NULL, &dlg);
if (status != PJ_SUCCESS) {
ERROR("Unable to create dialog \n");
return PJ_FALSE;
}
// Add credential for auth.
if (acc->hasCredentials() and pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->getCredentialCount(), acc->getCredInfo()) != PJ_SUCCESS) {
ERROR("Could not initialize credentials for subscribe session authentication");
}
/* Increment the dialog's lock otherwise when presence session creation
* fails the dialog will be destroyed prematurely.
*/
pjsip_dlg_inc_lock(dlg);
status = pjsip_pres_create_uac(dlg, &pres_callback, PJSIP_EVSUB_NO_EVENT_ID, &sub);
if (status != PJ_SUCCESS) {
pjsip_evsub_terminate(sub, PJ_FALSE); // = NULL;
WARN("Unable to create presence client", status);
/* This should destroy the dialog since there's no session
* referencing it
*/
if (dlg) {
pjsip_dlg_dec_lock(dlg);
}
// if (tmp_pool) pj_pool_release(tmp_pool);
// pj_log_pop_indent();
return PJ_SUCCESS;
}
#if 0
/* If account is locked to specific transport, then lock dialog
* to this transport too.
*/
if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
pjsip_tpselector tp_sel;
pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
pjsip_dlg_set_transport(buddy->dlg, &tp_sel);
}
/* Set route-set */
if (!pj_list_empty(&acc->route_set)) {
pjsip_dlg_set_route_set(buddy->dlg, &acc->route_set);
}
/* Set credentials */
if (acc->cred_cnt) {
pjsip_auth_clt_set_credentials(&buddy->dlg->auth_sess
acc->cred_cnt, acc->cred);
}
/* Set authentication preference */
pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
#endif
modId = ((SIPVoIPLink*) acc->getVoIPLink())->getModId();
pjsip_evsub_set_mod_data(sub, modId, this);
status = pjsip_pres_initiate(sub, -1, &tdata);
if (status != PJ_SUCCESS) {
if (dlg)
pjsip_dlg_dec_lock(dlg);
if (sub) {
pjsip_pres_terminate(sub, PJ_FALSE);
}
pjsip_evsub_terminate(sub, PJ_FALSE); // = NULL;
WARN("Unable to create initial SUBSCRIBE", status);
// if (tmp_pool) pj_pool_release(tmp_pool);
return PJ_SUCCESS;
}
// pjsua_process_msg_data(tdata, NULL);
status = pjsip_pres_send_request(sub, tdata);
if (status != PJ_SUCCESS) {
if (dlg)
pjsip_dlg_dec_lock(dlg);
if (sub) {
pjsip_pres_terminate(sub, PJ_FALSE);
sub = NULL;
}
WARN("Unable to send initial SUBSCRIBE", status);
// if (tmp_pool) pj_pool_release(tmp_pool);
return PJ_SUCCESS;
}
pjsip_dlg_dec_lock(dlg);
// if (tmp_pool) pj_pool_release(tmp_pool);
return PJ_SUCCESS;
}
bool PresSubClient::subscribe() {
monitor = true;
return ((updateSubscription() == PJ_SUCCESS))? true : false;
}
bool PresSubClient::unsubscribe() {
monitor = false;
return ((updateSubscription() == PJ_SUCCESS))? true : false;
}
bool PresSubClient::match(PresSubClient *b){
//return !(strcmp(b->getURI(),getURI()));
return (b->getURI()==getURI());
}
/*
* Copyright (C) 2012, 2013 LOTES TM LLC
* Author : Andrey Loukhnov <aol.nnov@gmail.com>
* Author : Patrick Keroulas <patrick.keroulas@savoirfairelinux.com>
* This file is a part of pult5-voip
*
* pult5-voip 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 3 of the License, or
* (at your option) any later version.
*
* pult5-voip 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 programm. If not, see <http://www.gnu.org/licenses/>.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify pult5-voip, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, LOTES-TM LLC
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#ifndef SIPBUDDY_H
#define SIPBUDDY_H
#include <pjsip-simple/presence.h>
#include <pj/timer.h>
#include <pj/pool.h>
#include <string>
#include <pjsip-simple/evsub.h>
#include <pjsip-simple/evsub_msg.h>
#include <pjsip/sip_endpoint.h>
#include <pjsip/sip_transport.h>
#include "noncopyable.h"
class SIPAccount;
/**
* Transaction functions of event subscription client side.
*/
static void buddy_evsub_on_state(pjsip_evsub *sub, pjsip_event *event);
static void buddy_evsub_on_tsx_state(pjsip_evsub *sub,
pjsip_transaction *tsx,
pjsip_event *event);
static void buddy_evsub_on_rx_notify(pjsip_evsub *sub,
pjsip_rx_data *rdata,
int *p_st_code,
pj_str_t **p_st_text,
pjsip_hdr *res_hdr,
pjsip_msg_body **p_body);
static void buddy_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry);
class PresSubClient {
public:
/**
* Constructor
* @param uri SIP uri of remote user that we want to subscribe,
*/
PresSubClient(const std::string &uri, SIPAccount *acc);
/**
* Destructor.
* Process the the unsubscription before the destruction.
*/
~PresSubClient();
/**
* Compare with another buddy's uris.
* @param b Other buddy pointer
*/
bool match(PresSubClient *b);
/**
* The PBX must approve the subrciption before the buddy is added in the buddy list.
*/
void accept();
/**
* Send a SUBCRIBE to the PXB or directly to a buddy in the IP2IP context.
*/
bool subscribe();
/**
* Send a SUBCRIBE to the PXB or directly to a buddy in the IP2IP context but
* the 0s timeout make the dialog expire immediatly.
*/
bool unsubscribe();
/**
* Return the monitor variable.
*/
bool isSubscribed();
/**
* Return the buddy URI
*/
std::string getURI();
friend void buddy_evsub_on_state(pjsip_evsub *sub, pjsip_event *event);
friend void buddy_evsub_on_tsx_state(pjsip_evsub *sub,
pjsip_transaction *tsx,
pjsip_event *event);
friend void buddy_evsub_on_rx_notify(pjsip_evsub *sub,
pjsip_rx_data *rdata,
int *p_st_code,
pj_str_t **p_st_text,
pjsip_hdr *res_hdr,
pjsip_msg_body **p_body);
friend void buddy_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry);
/**
* TODO: explain this:
*/
void incLock() {
lock_count++;
}
void decLock() {
lock_count--;
}
private:
NON_COPYABLE(PresSubClient);
/**
* Plan a retry or a renew a subscription.
* @param reschedule Allow for reschedule.
* @param msec Delay value in milliseconds.
*/
void rescheduleTimer(bool reschedule, unsigned msec);
/**
* Callback after a presence notification was received.
* Tranfert info to the SIP account.
*/
void reportPresence();
/**
* Process the un/subscribe request transmission.
*/
pj_status_t updateSubscription();
/*
* Compare the reason of a transaction end with the given string.
*/
bool isTermReason(std::string);
/**
* return the code after a transaction is terminated.
*/
unsigned getTermCode();
SIPAccount *acc; /**< Associated SIP account pointer */
pj_str_t uri; /**< Buddy URI. */
pj_str_t contact; /**< Contact learned from subscrp. */
pj_str_t display; /**< Buddy display name. */
pjsip_dialog *dlg; /**< The underlying dialog. */
pj_bool_t monitor; /**< Should we monitor? */
pj_str_t name; /**< Buddy name. */
pj_caching_pool cp_;
pj_pool_t *pool; /**< Pool for this buddy. */
pjsip_pres_status status; /**< Buddy presence status. */
pjsip_evsub *sub; /**< Buddy presence subscription */
unsigned term_code; /**< Subscription termination code */
pj_str_t term_reason;/**< Subscription termination reason */
pj_timer_entry timer; /**< Resubscription timer */
void *user_data; /**< Application data. */
int lock_count;
};
#endif /* SIPBUDDY_H */
/*
* Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
*
* Author: Patrick Keroulas <patrick.keroulas@savoirfairelinux.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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include "pjsip/sip_multipart.h"
#include "sipvoiplink.h"
#include "manager.h"
#include "sippresence.h"
#include "logger.h"
#include "pres_sub_server.h"
/* Callback called when *server* subscription state has changed. */
void pres_evsub_on_srv_state(pjsip_evsub *sub, pjsip_event *event) {
pjsip_rx_data *rdata = event->body.rx_msg.rdata;
if(!rdata) {
DEBUG("Presence_subscription_server estate has changed but no rdata.");
return;
}
PJ_UNUSED_ARG(event);
SIPPresence * pres = Manager::instance().getSipAccount("IP2IP")->getPresence();
pres->lock();
PresSubServer *presSubServer = (PresSubServer *) pjsip_evsub_get_mod_data(sub,pres->getModId());
DEBUG("Presence_subscription_server to %s is %s", presSubServer->remote, pjsip_evsub_get_state_name(sub));
if (presSubServer) {
pjsip_evsub_state state;
state = pjsip_evsub_get_state(sub);
if (state == PJSIP_EVSUB_STATE_TERMINATED) {
pjsip_evsub_set_mod_data(sub, pres->getModId(), NULL);
pres->removePresSubServer(presSubServer);
}
/* TODO check if other cases should be handled*/
}
pres->unlock();
}
pj_bool_t pres_on_rx_subscribe_request(pjsip_rx_data *rdata) {
pjsip_method *method = &rdata->msg_info.msg->line.req.method;
pj_str_t *str = &method->name;
std::string request(str->ptr, str->slen);
// pj_str_t contact;
pj_status_t status;
pjsip_dialog *dlg;
pjsip_evsub *sub;
pjsip_evsub_user pres_cb;
pjsip_tx_data *tdata;
pjsip_expires_hdr *expires_hdr;
pjsip_status_code st_code;
pj_str_t reason;
pres_msg_data msg_data;
pjsip_evsub_state ev_state;
/* Only hande incoming subscribe messages should be processed here.
* Otherwise we return FALSE to let other modules handle it */
if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method()) != 0)
return PJ_FALSE;
/* debug msg */
std::string name(rdata->msg_info.to->name.ptr, rdata->msg_info.to->name.slen);
std::string server(rdata->msg_info.from->name.ptr, rdata->msg_info.from->name.slen);
DEBUG("Incomming pres_on_rx_subscribe_request for %s, name:%s, server:%s."
, request.c_str()
, name.c_str()
, server.c_str());
/* get parents*/
std::string accountId = "IP2IP"; /* this code is only used for IP2IP accounts */
SIPAccount *acc = (SIPAccount *) Manager::instance().getSipAccount(accountId);
pjsip_endpoint *endpt = ((SIPVoIPLink*) acc->getVoIPLink())->getEndpoint();
SIPPresence * pres = acc->getPresence();
pres->lock();
/* Create UAS dialog: */
std::string c(acc->getContactHeader());
const pj_str_t contact = pj_str((char*) c.c_str());
status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, &contact, &dlg);
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
WARN("Unable to create UAS dialog for subscription: %s [status=%d]", errmsg, status);
pres->unlock();
pjsip_endpt_respond_stateless(endpt, rdata, 400, NULL, NULL, NULL);
return PJ_TRUE;
}
/* Init callback: */
pj_bzero(&pres_cb, sizeof(pres_cb));
pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
/* Create server presence subscription: */
status = pjsip_pres_create_uas(dlg, &pres_cb, rdata, &sub);
if (status != PJ_SUCCESS) {
int code = PJSIP_ERRNO_TO_SIP_STATUS(status);
pjsip_tx_data *tdata;
WARN("Unable to create server subscription %d", status);
if (code == 599 || code > 699 || code < 300) {
code = 400;
}
status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata);
if (status == PJ_SUCCESS) {
status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata);
}
pres->unlock();
return PJ_FALSE;
}
/* Attach our data to the subscription: */
char* remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri, remote, PJSIP_MAX_URL_SIZE);
if (status < 1)
pj_ansi_strcpy(remote, "<-- url is too long-->");
else
remote[status] = '\0';
//pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, dlg->local.info->uri, contact.ptr, PJSIP_MAX_URL_SIZE);
/* Create a new PresSubServer server and wait for client approve */
PresSubServer *presSubServer = new PresSubServer(pres, sub, remote, dlg);
pjsip_evsub_set_mod_data(sub, pres->getModId(), presSubServer);
pres->reportNewPresSubServerRequest(presSubServer); // Notify the client.
pres->addPresSubServer(presSubServer);
/* Capture the value of Expires header. */
expires_hdr = (pjsip_expires_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
if (expires_hdr)
presSubServer->setExpires(expires_hdr->ivalue);
else
presSubServer->setExpires(-1);
st_code = (pjsip_status_code) 200;
reason = pj_str("OK");
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);
/* Create and send 2xx response to the SUBSCRIBE request: */
status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
if (status != PJ_SUCCESS) {
WARN("Unable to accept presence subscription %d", status);
pjsip_pres_terminate(sub, PJ_FALSE);
pres->unlock();
return PJ_FALSE;
}
// Unsubscribe case
ev_state = PJSIP_EVSUB_STATE_ACTIVE;
if (presSubServer->getExpires() == 0){
ev_state = PJSIP_EVSUB_STATE_TERMINATED;
pres->unlock();
return PJ_TRUE;
}
/*Send notify immediatly. Replace real status with fake.*/
// pjsip_pres_set_status(sub, pres->getStatus()); // real status
// fake temporary status
pjrpid_element rpid = {
PJRPID_ELEMENT_TYPE_PERSON,
pj_str("20"),
PJRPID_ACTIVITY_UNKNOWN,
pj_str("Your subscription was received and waits for approval.")
};
pjsip_pres_status fake_status_data;
pj_bzero(&fake_status_data, sizeof(pjsip_pres_status));
fake_status_data.info_cnt = 1;
fake_status_data.info[0].basic_open = false;
fake_status_data.info[0].id = pj_str("0"); /* todo: tuplie_id*/
pj_memcpy(&fake_status_data.info[0].rpid, &rpid,sizeof(pjrpid_element));
pjsip_pres_set_status(sub, &fake_status_data);
/* Create and send the the first NOTIFY to active subscription: */
pj_str_t stateStr = pj_str("");
tdata = NULL;
status = pjsip_pres_notify(sub, ev_state, &stateStr, &reason, &tdata);
if (status == PJ_SUCCESS) {
pres->fillDoc(tdata, &msg_data);
status = pjsip_pres_send_request(sub, tdata);
}
if (status != PJ_SUCCESS) {
WARN("Unable to create/send NOTIFY %d", status);
pjsip_pres_terminate(sub, PJ_FALSE);
pres->unlock();
return status;
}
pres->unlock();
return PJ_TRUE;
}
PresSubServer::PresSubServer(SIPPresence * pres, pjsip_evsub *evsub, char *r, pjsip_dialog *d)
: pres_(pres)
, sub(evsub)
, remote(r)
, dlg(d)
, expires(-1)
, approved(false)
{}
PresSubServer::~PresSubServer(){
//TODO: check if evsub needs to be forced TERMINATED.
}
void PresSubServer::setExpires(int ms) {
expires = ms;
}
int PresSubServer::getExpires(){
return expires;
}
/*SIPPresence * PresSubServer::getPresence(){
return pres_;
}*/
bool PresSubServer::matches(char *s){
// servers match if they have the same remote uri and the account ID.
return (!(strcmp(remote,s))) ;
}
void PresSubServer::approve(const bool& flag){
approved = flag;
// attach the real status data
pjsip_pres_set_status(sub, pres_->getStatus());
}
void PresSubServer::notify() {
/* Only send NOTIFY once subscription is active. Some subscriptions
* may still be in NULL (when app is adding a new buddy while in the
* on_incoming_subscribe() callback) or PENDING (when user approval is
* being requested) state and we don't send NOTIFY to these subs until
* the user accepted the request.
*/
if ((pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE) && (approved)) {
DEBUG("Notifying %s.", remote);
pjsip_tx_data *tdata;
pjsip_pres_set_status(sub, pres_->getStatus());
if (pjsip_pres_current_notify(sub, &tdata) == PJ_SUCCESS) {
// add msg header and send
pres_->fillDoc(tdata, NULL);
pjsip_pres_send_request(sub, tdata);
}
else{
WARN("Unable to create/send NOTIFY");
pjsip_pres_terminate(sub, PJ_FALSE);
}
}
}
/*
* Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
*
* Author: Patrick Keroulas <patrick.keroulas@savoirfairelinux.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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#ifndef SERVERPRESENCESUB_H
#define SERVERPRESENCESUB_H
#include <pj/string.h>
#include <pjsip/sip_types.h>
#include <pjsip-simple/evsub.h>
#include <pjsip-simple/presence.h>
#include <pjsip/sip_module.h>
#include "src/noncopyable.h"
extern pj_bool_t pres_on_rx_subscribe_request(pjsip_rx_data *rdata);
static pjsip_module mod_presence_server = {
NULL, NULL, /* prev, next. */
pj_str("mod-presence-server"), /* Name. */
-1, /* Id */
PJSIP_MOD_PRIORITY_DIALOG_USAGE,
NULL, /* load() */
NULL, /* start() */
NULL, /* stop() */
NULL, /* unload() */
&pres_on_rx_subscribe_request, /* on_rx_request() */
NULL, /* on_rx_response() */
NULL, /* on_tx_request. */
NULL, /* on_tx_response() */
NULL, /* on_tsx_state() */
};
class SIPpresence;
class PresSubServer {
public:
PresSubServer(SIPPresence * pres, pjsip_evsub *evsub, char *r,pjsip_dialog *d);
~PresSubServer();
/* TODO: add '< >' to URI for consistance*/
char *remote; /**< Remote URI. */
/*
* Acces to the evsub expire variable.
* It was recieved in the SUBSCRIBE request.
*/
void setExpires(int ms);
int getExpires();
/*
* Match method
* s is the URI (remote)
*/
bool matches(char *s);
/*
* Allow the subscriber for being notified.
*/
void approve(const bool& flag);
/*
* Notify subscriber with the pres_status_date of the account
*/
void notify();
friend void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event);
friend pj_bool_t pres_on_rx_subscribe_request(pjsip_rx_data *rdata);
private:
NON_COPYABLE(PresSubServer);
SIPPresence *pres_;
pjsip_evsub *sub; /**< The evsub. */
pjsip_dialog *dlg; /**< Dialog. */
int expires; /**< "expires" value in the request. */
bool approved; /**< The client approved this subscription*/
};
#endif /* SERVERPRESENCESUB_H */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment