mirror of https://github.com/sipwise/sems.git
Development sponsored by Sipwise GmbH. Conflicts: apps/dsm/DSMCall.cpp apps/dsm/DSMStateEngine.h core/sip/defs.hsayer/1.4-spce2.6
parent
1eb5be3f7b
commit
d9795df0b8
@ -0,0 +1,10 @@
|
||||
plug_in_name = mod_subscription
|
||||
|
||||
DSMPATH ?= ../..
|
||||
|
||||
module_ldflags =
|
||||
module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I$(DSMPATH)
|
||||
|
||||
COREPATH ?=$(DSMPATH)/../../core
|
||||
lib_full_name = $(DSMPATH)/mods/lib/$(lib_name)
|
||||
include $(DSMPATH)/mods/Makefile.dsm_module
|
||||
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2012 FRAFOS GmbH
|
||||
*
|
||||
* Development sponsored by Sipwise GmbH.
|
||||
*
|
||||
* This file is part of SEMS, a free SIP media server.
|
||||
*
|
||||
* SEMS 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 released under
|
||||
* the GPL with the additional exemption that compiling, linking,
|
||||
* and/or using OpenSSL is allowed.
|
||||
*
|
||||
* For a license to use the SEMS software under conditions
|
||||
* other than those described here, or to purchase support for this
|
||||
* software, please contact iptel.org by e-mail at the following addresses:
|
||||
* info@iptel.org
|
||||
*
|
||||
* SEMS 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 "ModSubscription.h"
|
||||
#include "log.h"
|
||||
#include "AmUtils.h"
|
||||
|
||||
#include "AmSipSubscriptionContainer.h"
|
||||
|
||||
SC_EXPORT(MOD_CLS_NAME);
|
||||
|
||||
MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) {
|
||||
|
||||
DEF_CMD("subscription.create", SIPSUBCreateAction);
|
||||
DEF_CMD("subscription.refresh", SIPSUBRefreshAction);
|
||||
DEF_CMD("subscription.remove", SIPSUBRemoveAction);
|
||||
|
||||
} MOD_ACTIONEXPORT_END;
|
||||
|
||||
MOD_CONDITIONEXPORT_NONE(MOD_CLS_NAME);
|
||||
|
||||
CONST_ACTION_2P(SIPSUBCreateAction, ',', true);
|
||||
EXEC_ACTION_START(SIPSUBCreateAction) {
|
||||
string params_s = resolveVars(par1, sess, sc_sess, event_params);
|
||||
// string basedir = resolveVars(par2, sess, sc_sess, event_params);
|
||||
|
||||
AmSipSubscriptionInfo info(sc_sess->var[params_s+".domain"],
|
||||
sc_sess->var[params_s+".user"],
|
||||
sc_sess->var[params_s+".from_user"],
|
||||
sc_sess->var[params_s+".pwd"],
|
||||
sc_sess->var[params_s+".proxy"],
|
||||
sc_sess->var[params_s+".event"]);
|
||||
info.accept = sc_sess->var[params_s+".accept"];
|
||||
info.id = sc_sess->var[params_s+".id"];
|
||||
unsigned int expires = 0;
|
||||
|
||||
if (sc_sess->var.find(params_s+".expires") != sc_sess->var.end()) {
|
||||
str2i(sc_sess->var[params_s+".expires"], expires);
|
||||
}
|
||||
|
||||
string handle = AmSipSubscriptionContainer::instance()->
|
||||
createSubscription(info, sess->getLocalTag(), expires);
|
||||
|
||||
DBG("got handle '%s'\n", handle.c_str());
|
||||
sc_sess->var[params_s+".handle"] = handle;
|
||||
if (handle.empty()) {
|
||||
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
|
||||
sc_sess->SET_STRERROR("could not create subscription\n");
|
||||
} else {
|
||||
sc_sess->CLR_ERRNO;
|
||||
}
|
||||
} EXEC_ACTION_END;
|
||||
|
||||
|
||||
|
||||
CONST_ACTION_2P(SIPSUBRefreshAction, ',', true);
|
||||
EXEC_ACTION_START(SIPSUBRefreshAction) {
|
||||
string handle = resolveVars(par1, sess, sc_sess, event_params);
|
||||
string expires = resolveVars(par2, sess, sc_sess, event_params);
|
||||
unsigned int expires_i = 0;
|
||||
if (!expires.empty())
|
||||
str2i(expires, expires_i);
|
||||
|
||||
DBG("refreshing subscription with handle '%s'\n", handle.c_str());
|
||||
if (!AmSipSubscriptionContainer::instance()->refreshSubscription(handle, expires_i)) {
|
||||
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
|
||||
sc_sess->SET_STRERROR("could not refresh subscription\n");
|
||||
} else {
|
||||
sc_sess->CLR_ERRNO;
|
||||
}
|
||||
} EXEC_ACTION_END;
|
||||
|
||||
EXEC_ACTION_START(SIPSUBRemoveAction) {
|
||||
string handle = resolveVars(arg, sess, sc_sess, event_params);
|
||||
DBG("removing subscription with handle '%s'\n", handle.c_str());
|
||||
AmSipSubscriptionContainer::instance()->removeSubscription(handle);
|
||||
} EXEC_ACTION_END;
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2012 FRAFOS GmbH
|
||||
*
|
||||
* Development sponsored by Sipwise GmbH.
|
||||
*
|
||||
* This file is part of SEMS, a free SIP media server.
|
||||
*
|
||||
* SEMS 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 released under
|
||||
* the GPL with the additional exemption that compiling, linking,
|
||||
* and/or using OpenSSL is allowed.
|
||||
*
|
||||
* For a license to use the SEMS software under conditions
|
||||
* other than those described here, or to purchase support for this
|
||||
* software, please contact iptel.org by e-mail at the following addresses:
|
||||
* info@iptel.org
|
||||
*
|
||||
* SEMS 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
|
||||
*/
|
||||
#ifndef _MOD_SUBSCRIPTION_H
|
||||
#define _MOD_SUBSCRIPTION_H
|
||||
#include "DSMModule.h"
|
||||
#include "DSMSession.h"
|
||||
|
||||
#define MOD_CLS_NAME SCSubscriptionModule
|
||||
|
||||
DECLARE_MODULE(MOD_CLS_NAME);
|
||||
|
||||
DEF_ACTION_2P(SIPSUBCreateAction);
|
||||
DEF_ACTION_2P(SIPSUBRefreshAction);
|
||||
DEF_ACTION_1P(SIPSUBRemoveAction);
|
||||
#endif
|
||||
@ -0,0 +1,396 @@
|
||||
/*
|
||||
* Copyright (C) 2012 FRAFOS GmbH
|
||||
*
|
||||
* This file is part of SEMS, a free SIP media server.
|
||||
*
|
||||
* SEMS 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 released under
|
||||
* the GPL with the additional exemption that compiling, linking,
|
||||
* and/or using OpenSSL is allowed.
|
||||
*
|
||||
* For a license to use the SEMS software under conditions
|
||||
* other than those described here, or to purchase support for this
|
||||
* software, please contact iptel.org by e-mail at the following addresses:
|
||||
* info@iptel.org
|
||||
*
|
||||
* SEMS 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 "AmSipSubscription.h"
|
||||
#include "AmSession.h"
|
||||
#include "AmSessionContainer.h"
|
||||
AmSipSubscription::AmSipSubscription(const string& handle,
|
||||
const AmSipSubscriptionInfo& info,
|
||||
const string& sess_link)
|
||||
: info(info),
|
||||
dlg(sess_checkpoint, this),
|
||||
cred(info.domain, info.from_user, info.pwd),
|
||||
sub_begin(0),
|
||||
sub_expires(0),
|
||||
wanted_expires(0),
|
||||
sess_link(sess_link),
|
||||
seh(NULL),
|
||||
sub_state(SipSubStateIdle)
|
||||
{
|
||||
|
||||
setReqFromInfo();
|
||||
req.from_tag = handle;
|
||||
|
||||
// clear dlg.callid? ->reregister?
|
||||
#ifdef HAVE_SEMS_OA
|
||||
dlg.setOAEnabled(false);
|
||||
dlg.initFromLocalRequest(req);
|
||||
#else
|
||||
dlg.updateStatusFromLocalRequest(req);
|
||||
#endif
|
||||
dlg.cseq = 50;
|
||||
}
|
||||
|
||||
AmSipSubscription::~AmSipSubscription() {
|
||||
setSessionEventHandler(NULL);
|
||||
}
|
||||
|
||||
void AmSipSubscription::setReqFromInfo() {
|
||||
req.user = info.user;
|
||||
req.method = "SUBSCRIBE";
|
||||
req.domain = info.domain;
|
||||
req.r_uri = "sip:"+info.user+"@"+info.domain;
|
||||
req.from = "<sip:"+info.from_user+"@"+info.domain+">";
|
||||
req.from_uri = "sip:"+info.from_user+"@"+info.domain;
|
||||
req.to = "<sip:"+info.user+"@"+info.domain+">";
|
||||
req.to_tag = "";
|
||||
req.callid = AmSession::getNewId();
|
||||
}
|
||||
|
||||
void AmSipSubscription::setSubscriptionInfo(const AmSipSubscriptionInfo& _info) {
|
||||
DBG("updating subscription info for '%s@%s'\n",
|
||||
_info.user.c_str(), _info.domain.c_str());
|
||||
info = _info;
|
||||
|
||||
setReqFromInfo();
|
||||
|
||||
// to trigger setting dlg identifiers
|
||||
dlg.callid.clear();
|
||||
dlg.contact_uri.clear();
|
||||
|
||||
#ifdef HAVE_SEMS_OA
|
||||
dlg.initFromLocalRequest(req);
|
||||
#else
|
||||
dlg.updateStatusFromLocalRequest(req);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AmSipSubscription::setExpiresInterval(unsigned int desired_expires) {
|
||||
wanted_expires = desired_expires;
|
||||
}
|
||||
|
||||
void AmSipSubscription::setSessionEventHandler(AmSessionEventHandler* new_seh) {
|
||||
if (seh)
|
||||
delete seh;
|
||||
seh = new_seh;
|
||||
}
|
||||
|
||||
string AmSipSubscription::getDescription() {
|
||||
return "'"+info.user+"@"+info.domain+", Event: "+info.event+"'";
|
||||
}
|
||||
|
||||
string AmSipSubscription::getSubscribeHdrs() {
|
||||
string hdrs;
|
||||
hdrs += SIP_HDR_COLSP(SIP_HDR_EVENT) + info.event;
|
||||
if (!info.id.empty())
|
||||
hdrs += ";id="+info.id;
|
||||
hdrs += CRLF;
|
||||
if (!info.accept.empty()) {
|
||||
hdrs += SIP_HDR_COLSP(SIP_HDR_ACCEPT) + info.accept + CRLF;
|
||||
}
|
||||
if (wanted_expires) {
|
||||
hdrs += SIP_HDR_COLSP(SIP_HDR_EXPIRES) + int2str(wanted_expires) + CRLF;
|
||||
}
|
||||
return hdrs;
|
||||
}
|
||||
|
||||
bool AmSipSubscription::doSubscribe()
|
||||
{
|
||||
bool res = true;
|
||||
|
||||
req.to_tag = "";
|
||||
dlg.remote_tag = "";
|
||||
dlg.remote_uri = req.r_uri;
|
||||
|
||||
// set outbound proxy as next hop
|
||||
if (!info.proxy.empty()) {
|
||||
dlg.outbound_proxy = info.proxy;
|
||||
} else if (!AmConfig::OutboundProxy.empty()) {
|
||||
dlg.outbound_proxy = AmConfig::OutboundProxy;
|
||||
}
|
||||
|
||||
if (dlg.sendRequest(req.method,
|
||||
#ifdef HAVE_SEMS_OA
|
||||
NULL,
|
||||
#else
|
||||
"", "",
|
||||
#endif
|
||||
getSubscribeHdrs()) < 0) {
|
||||
WARN("failed to send subscription to '%s' (proxy '%s').\n",
|
||||
dlg.remote_uri.c_str(), dlg.outbound_proxy.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (sub_state) {
|
||||
case SipSubStateIdle:
|
||||
case SipSubStatePending:
|
||||
case SipSubStateTerminated:
|
||||
sub_state = SipSubStatePending;
|
||||
break;
|
||||
case SipSubStateSubscribed: break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool AmSipSubscription::reSubscribe()
|
||||
{
|
||||
if (dlg.sendRequest(req.method, NULL, getSubscribeHdrs()) < 0) {
|
||||
WARN("failed to send subscription to '%s' (proxy '%s').\n",
|
||||
dlg.remote_uri.c_str(), dlg.outbound_proxy.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AmSipSubscription::doUnsubscribe()
|
||||
{
|
||||
if (sub_state == SipSubStateTerminated || sub_state == SipSubStateIdle) {
|
||||
DBG("not subscribed - not unsubscribing\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
req.to_tag = "";
|
||||
dlg.remote_tag = "";
|
||||
dlg.remote_uri = req.r_uri;
|
||||
|
||||
// set outbound proxy as next hop
|
||||
if (!info.proxy.empty()) {
|
||||
dlg.outbound_proxy = info.proxy;
|
||||
} else if (!AmConfig::OutboundProxy.empty())
|
||||
dlg.outbound_proxy = AmConfig::OutboundProxy;
|
||||
//else
|
||||
// dlg.outbound_proxy = "";
|
||||
|
||||
string hdrs;
|
||||
hdrs += SIP_HDR_COLSP(SIP_HDR_EVENT) + info.event;
|
||||
if (!info.id.empty())
|
||||
hdrs += ";id="+info.id;
|
||||
hdrs += CRLF;
|
||||
if (!info.accept.empty()) {
|
||||
hdrs += SIP_HDR_COLSP(SIP_HDR_ACCEPT) + info.accept + CRLF;
|
||||
}
|
||||
hdrs += SIP_HDR_COLSP(SIP_HDR_EXPIRES) "0" CRLF;
|
||||
|
||||
if (dlg.sendRequest(req.method, NULL, hdrs) < 0) {
|
||||
WARN("failed to send unsubscription to '%s' (proxy '%s').\n",
|
||||
dlg.remote_uri.c_str(), dlg.outbound_proxy.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#ifdef HAVE_SEMS_OA
|
||||
void AmSipSubscription::onSendRequest(AmSipRequest& req, int flags)
|
||||
{
|
||||
if (seh)
|
||||
seh->onSendRequest(req,flags);
|
||||
}
|
||||
|
||||
void AmSipSubscription::onSendReply(AmSipReply& reply, int flags) {
|
||||
if (seh)
|
||||
seh->onSendReply(reply,flags);
|
||||
}
|
||||
#else
|
||||
void AmSipSubscription::onSendRequest(const string& method, const string& content_type,
|
||||
const string& body, string& hdrs, int flags,
|
||||
unsigned int cseq) {
|
||||
if (seh)
|
||||
seh->onSendRequest(method, content_type, body, hdrs, flags, cseq);
|
||||
}
|
||||
|
||||
void AmSipSubscription::onSendReply(const AmSipRequest& req, unsigned int code,
|
||||
const string& reason, const string& content_type,
|
||||
const string& body, string& hdrs, int flags) {
|
||||
if (seh)
|
||||
seh->onSendReply(req, code, reason, content_type, body, hdrs, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
AmSipSubscription::SipSubscriptionState AmSipSubscription::getState() {
|
||||
return sub_state;
|
||||
}
|
||||
|
||||
unsigned int AmSipSubscription::getExpiresLeft() {
|
||||
if (sub_state != SipSubStateSubscribed)
|
||||
return 0;
|
||||
|
||||
long diff = sub_begin + sub_expires - time(NULL);
|
||||
if (diff < 0)
|
||||
return 0;
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
void AmSipSubscription::onSubscriptionExpired() {
|
||||
if (sess_link.length()) {
|
||||
AmSessionContainer::instance()->
|
||||
postEvent(sess_link, new SIPSubscriptionEvent(SIPSubscriptionEvent::
|
||||
SubscriptionTimeout, req.from_tag));
|
||||
}
|
||||
DBG("Subscription '%s' expired.\n", getDescription().c_str());
|
||||
}
|
||||
|
||||
void AmSipSubscription::onRxReply(const AmSipReply& reply) {
|
||||
#ifdef HAVE_SEMS_OA
|
||||
dlg.onRxReply(reply);
|
||||
#else
|
||||
dlg.updateStatus(reply);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AmSipSubscription::onRxRequest(const AmSipRequest& req) {
|
||||
#ifdef HAVE_SEMS_OA
|
||||
dlg.onRxRequest(req);
|
||||
#else
|
||||
dlg.updateStatus(req);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AmSipSubscription::onSipReply(const AmSipReply& reply,
|
||||
#ifdef HAVE_SEMS_OA
|
||||
AmSipDialog::Status old_dlg_status
|
||||
#else
|
||||
int old_dlg_status, const string& trans_method
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if ((seh!=NULL) && seh->onSipReply(reply, old_dlg_status
|
||||
#ifndef HAVE_SEMS_OA
|
||||
, trans_method
|
||||
#endif
|
||||
))
|
||||
return;
|
||||
|
||||
if (reply.code < 200) {
|
||||
DBG("Provisional reply received for subscription '%s'\n", getDescription().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if ((reply.code>=200)&&(reply.code<300)) {
|
||||
DBG("Positive reply to SUBSCRIBE\n");
|
||||
|
||||
unsigned int expires_i = 0;
|
||||
string expires = getHeader(reply.hdrs, SIP_HDR_EXPIRES);
|
||||
if (expires.length()) {
|
||||
str2i(trim(expires, " \t"), expires_i);
|
||||
}
|
||||
|
||||
AmSessionContainer::instance()->
|
||||
postEvent(sess_link,
|
||||
new SIPSubscriptionEvent(SIPSubscriptionEvent::SubscribeActive,
|
||||
req.from_tag, expires_i,
|
||||
reply.code, reply.reason));
|
||||
} else if (reply.code >= 300) {
|
||||
DBG("Subscription '%s' failed\n", getDescription().c_str());
|
||||
if (sess_link.length()) {
|
||||
AmSessionContainer::instance()->
|
||||
postEvent(sess_link,
|
||||
new SIPSubscriptionEvent(SIPSubscriptionEvent::SubscribeFailed,
|
||||
req.from_tag, 0,
|
||||
reply.code, reply.reason));
|
||||
}
|
||||
sub_state = SipSubStateTerminated;
|
||||
}
|
||||
}
|
||||
|
||||
void AmSipSubscription::onSipRequest(const AmSipRequest& req) {
|
||||
DBG("SIP Request received: '%s'\n", req.print().c_str());
|
||||
if (req.method != SIP_METH_NOTIFY) {
|
||||
WARN("received %s Request in subscription???\n", req.method.c_str());
|
||||
dlg.reply(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR); // todo: other err code?
|
||||
sub_state = SipSubStateTerminated;
|
||||
return;
|
||||
}
|
||||
|
||||
string h_subscription_state = getHeader(req.hdrs, SIP_HDR_SUBSCRIPTION_STATE);
|
||||
if (h_subscription_state.empty()) {
|
||||
WARN("received NOTIFY without Subscription-State (hdrs='%s')\n", req.hdrs.c_str());
|
||||
dlg.reply(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR); // todo: other err code?
|
||||
sub_state = SipSubStateTerminated;
|
||||
return;
|
||||
}
|
||||
size_t pos = h_subscription_state.find(";");
|
||||
string subscription_state = h_subscription_state;
|
||||
string subscription_state_params;
|
||||
unsigned int expires = 0;
|
||||
string reason;
|
||||
|
||||
if (pos != string::npos) {
|
||||
subscription_state = h_subscription_state.substr(0, pos);
|
||||
subscription_state_params = h_subscription_state.substr(pos+1);
|
||||
}
|
||||
vector<string> params = explode(subscription_state_params, ";");
|
||||
for (vector<string>::iterator it=params.begin(); it != params.end(); it++) {
|
||||
vector<string> pv = explode(trim(*it, " \t"), "=");
|
||||
if (pv.size()) {
|
||||
if (trim(pv[0], " \t")=="expires") {
|
||||
if (pv.size()>1)
|
||||
str2i(trim(pv[1], " \t"), expires);
|
||||
} else if (trim(pv[0], " \t")=="reason") {
|
||||
if (pv.size()>1)
|
||||
reason = trim(pv[1], " \t");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DBG("subscription state: '%s', expires: %u, reason: '%s'\n",
|
||||
subscription_state.c_str(), expires, reason.c_str());
|
||||
|
||||
SIPSubscriptionEvent* sub_ev =
|
||||
new SIPSubscriptionEvent(SIPSubscriptionEvent::SubscribeFailed, req.from_tag);
|
||||
|
||||
if (subscription_state == "active") {
|
||||
sub_begin = time(0);
|
||||
sub_ev->expires = sub_expires = expires;
|
||||
sub_state = SipSubStateSubscribed;
|
||||
sub_ev->status = SIPSubscriptionEvent::SubscribeActive;
|
||||
} else if (subscription_state == "pending") {
|
||||
sub_begin = time(0);
|
||||
sub_expires = expires;
|
||||
sub_state = SipSubStatePending;
|
||||
sub_ev->status = SIPSubscriptionEvent::SubscribePending;
|
||||
} else if (subscription_state == "terminated") {
|
||||
sub_expires = 0;
|
||||
sub_state = SipSubStateTerminated;
|
||||
sub_ev->status = SIPSubscriptionEvent::SubscribeTerminated;
|
||||
} else {
|
||||
ERROR("unknown subscription_state '%s'\n", subscription_state.c_str());
|
||||
}
|
||||
|
||||
#ifdef HAVE_SEMS_OA
|
||||
if (!req.body.empty())
|
||||
sub_ev->notify_body.reset(new AmMimeBody(req.body));
|
||||
#else
|
||||
sub_ev->notify_body = req.body;
|
||||
#endif
|
||||
|
||||
DBG("posting event to session '%s'\n", sess_link.c_str());
|
||||
AmSessionContainer::instance()->postEvent(sess_link, sub_ev);
|
||||
|
||||
dlg.reply(req, 200, "OK");
|
||||
}
|
||||
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2012 FRAFOS GmbH
|
||||
*
|
||||
* Development sponsored by Sipwise GmbH.
|
||||
*
|
||||
* This file is part of SEMS, a free SIP media server.
|
||||
*
|
||||
* SEMS 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 released under
|
||||
* the GPL with the additional exemption that compiling, linking,
|
||||
* and/or using OpenSSL is allowed.
|
||||
*
|
||||
* For a license to use the SEMS software under conditions
|
||||
* other than those described here, or to purchase support for this
|
||||
* software, please contact iptel.org by e-mail at the following addresses:
|
||||
* info@iptel.org
|
||||
*
|
||||
* SEMS 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
|
||||
*/
|
||||
|
||||
#ifndef _AmSipSubscription_h_
|
||||
#define _AmSipSubscription_h_
|
||||
|
||||
#include <string>
|
||||
using std::string;
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "ampi/UACAuthAPI.h"
|
||||
#include "AmSessionEventHandler.h"
|
||||
#include "AmSdp.h"
|
||||
|
||||
#ifdef HAVE_SEMS_OA
|
||||
# include "AmMimeBody.h"
|
||||
#endif
|
||||
|
||||
struct AmSipSubscriptionInfo {
|
||||
string domain;
|
||||
string user;
|
||||
string from_user;
|
||||
string pwd;
|
||||
string proxy;
|
||||
string event;
|
||||
string accept;
|
||||
string id;
|
||||
|
||||
AmSipSubscriptionInfo(const string& domain,
|
||||
const string& user,
|
||||
const string& from_user,
|
||||
const string& pwd,
|
||||
const string& proxy,
|
||||
const string& event)
|
||||
: domain(domain),user(user),
|
||||
from_user(from_user),pwd(pwd),proxy(proxy),
|
||||
event(event)
|
||||
{ }
|
||||
};
|
||||
|
||||
struct SIPSubscriptionEvent : public AmEvent {
|
||||
|
||||
enum SubscriptionStatus {
|
||||
SubscribeActive=0,
|
||||
SubscribeFailed,
|
||||
SubscribeTerminated,
|
||||
SubscribePending,
|
||||
SubscriptionTimeout
|
||||
};
|
||||
|
||||
string handle;
|
||||
unsigned int code;
|
||||
string reason;
|
||||
SubscriptionStatus status;
|
||||
unsigned int expires;
|
||||
#ifdef HAVE_SEMS_OA
|
||||
std::auto_ptr<AmMimeBody> notify_body;
|
||||
#else
|
||||
string notify_body;
|
||||
#endif
|
||||
|
||||
SIPSubscriptionEvent(SubscriptionStatus status, const string& handle,
|
||||
unsigned int expires = 0,
|
||||
unsigned int code=0, const string& reason="")
|
||||
: AmEvent(E_SIP_SUBSCRIPTION), status(status), handle(handle),
|
||||
expires(expires), code(code), reason(reason)
|
||||
#ifdef HAVE_SEMS_OA
|
||||
,notify_body(0)
|
||||
#endif
|
||||
{}
|
||||
|
||||
const char* getStatusText() {
|
||||
switch (status) {
|
||||
case SubscribeActive: return "active";
|
||||
case SubscribeFailed: return "failed";
|
||||
case SubscribeTerminated: return "terminated";
|
||||
case SubscribePending: return "pending";
|
||||
case SubscriptionTimeout: return "timeout";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class AmSipSubscription
|
||||
: public AmSipDialogEventHandler,
|
||||
public DialogControl,
|
||||
public CredentialHolder
|
||||
{
|
||||
|
||||
public:
|
||||
enum SipSubscriptionState {
|
||||
SipSubStateIdle,
|
||||
SipSubStatePending,
|
||||
SipSubStateSubscribed,
|
||||
SipSubStateTerminated,
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
AmSipDialog dlg;
|
||||
AmSessionCheckpoint sess_checkpoint;
|
||||
UACAuthCred cred;
|
||||
|
||||
AmSipSubscriptionInfo info;
|
||||
|
||||
// session to post events to
|
||||
string sess_link;
|
||||
|
||||
AmSessionEventHandler* seh;
|
||||
|
||||
AmSipRequest req;
|
||||
|
||||
SipSubscriptionState sub_state;
|
||||
time_t sub_begin;
|
||||
unsigned int sub_expires;
|
||||
|
||||
unsigned int wanted_expires;
|
||||
|
||||
void setReqFromInfo();
|
||||
string getSubscribeHdrs();
|
||||
|
||||
void onSubscriptionExpired();
|
||||
|
||||
public:
|
||||
AmSipSubscription(const string& handle,
|
||||
const AmSipSubscriptionInfo& info,
|
||||
const string& sess_link);
|
||||
~AmSipSubscription();
|
||||
|
||||
void setSubscriptionInfo(const AmSipSubscriptionInfo& _info);
|
||||
string getDescription();
|
||||
|
||||
void setSessionEventHandler(AmSessionEventHandler* new_seh);
|
||||
|
||||
void setExpiresInterval(unsigned int desired_expires);
|
||||
|
||||
/** subscribe with the info set */
|
||||
bool doSubscribe();
|
||||
/** re-subscribe with the info set */
|
||||
bool reSubscribe();
|
||||
/** unsubscribe */
|
||||
bool doUnsubscribe();
|
||||
|
||||
/** return the state of the subscription */
|
||||
SipSubscriptionState getState();
|
||||
|
||||
/** return the expires left for the subscription */
|
||||
unsigned int getExpiresLeft();
|
||||
|
||||
const string& getEventSink() { return sess_link; }
|
||||
const string& getHandle() { return req.from_tag; }
|
||||
|
||||
void onRxReply(const AmSipReply& reply);
|
||||
void onRxRequest(const AmSipRequest& req);
|
||||
|
||||
#ifdef HAVE_SEMS_OA
|
||||
void onSipRequest(const AmSipRequest& req);
|
||||
void onSipReply(const AmSipReply& reply, AmSipDialog::Status old_dlg_status);
|
||||
#else
|
||||
void onSipRequest(const AmSipRequest& req);
|
||||
void onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method);
|
||||
|
||||
void onSendRequest(const string& method, const string& content_type, const string& body,
|
||||
string& hdrs, int flags, unsigned int cseq);
|
||||
void onSendReply(const AmSipRequest& req, unsigned int code, const string& reason,
|
||||
const string& content_type, const string& body, string& hdrs, int flags);
|
||||
#endif
|
||||
|
||||
// DialogControl if
|
||||
AmSipDialog* getDlg() { return &dlg; }
|
||||
// CredentialHolder
|
||||
UACAuthCred* getCredentials() { return &cred; }
|
||||
|
||||
void onInvite2xx(const AmSipReply&) {}
|
||||
void onRemoteDisappeared(const AmSipReply&) {}
|
||||
void onInvite1xxRel(const AmSipReply &){}
|
||||
void onNoAck(unsigned int) {}
|
||||
void onNoPrack(const AmSipRequest &, const AmSipReply &) {}
|
||||
void onPrack2xx(const AmSipReply &){}
|
||||
void onFailure(AmSipDialogEventHandler::FailureCause cause,
|
||||
const AmSipRequest*, const AmSipReply*){}
|
||||
bool getSdpOffer(AmSdp&) {return false;}
|
||||
bool getSdpAnswer(const AmSdp&, AmSdp&) {return false;}
|
||||
int onSdpCompleted(const AmSdp&, const AmSdp&) {return -1;}
|
||||
void onEarlySessionStart() {}
|
||||
void onSessionStart() {}
|
||||
};
|
||||
|
||||
typedef std::map<string, AmSipSubscription*> AmSipSubscriptionMap;
|
||||
typedef AmSipSubscriptionMap::iterator AmSipSubscriptionMapIter;
|
||||
typedef AmSipSubscriptionMap::const_iterator AmSipSubscriptionMapConstIter;
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2012 FRAFOS GmbH
|
||||
*
|
||||
* Development sponsored by Sipwise GmbH.
|
||||
*
|
||||
* This file is part of SEMS, a free SIP media server.
|
||||
*
|
||||
* SEMS 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 released under
|
||||
* the GPL with the additional exemption that compiling, linking,
|
||||
* and/or using OpenSSL is allowed.
|
||||
*
|
||||
* For a license to use the SEMS software under conditions
|
||||
* other than those described here, or to purchase support for this
|
||||
* software, please contact iptel.org by e-mail at the following addresses:
|
||||
* info@iptel.org
|
||||
*
|
||||
* SEMS 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 "AmSipSubscriptionContainer.h"
|
||||
#include "AmSession.h"
|
||||
#include "AmEventDispatcher.h"
|
||||
|
||||
#define SUBSCRIPTION_CONTAINER_EVQ_ID "_subscription_container_"
|
||||
|
||||
_AmSipSubscriptionContainer::_AmSipSubscriptionContainer()
|
||||
: initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
_AmSipSubscriptionContainer::~_AmSipSubscriptionContainer() {
|
||||
}
|
||||
|
||||
void _AmSipSubscriptionContainer::initialize() {
|
||||
if (!initialized) {
|
||||
// AmEventDispatcher::instance()->addEventQueue(SUBSCRIPTION_CONTAINER_EVQ_ID, this);
|
||||
}
|
||||
}
|
||||
|
||||
string _AmSipSubscriptionContainer::createSubscription(const AmSipSubscriptionInfo& info,
|
||||
const string& sess_link,
|
||||
unsigned int wanted_expires) {
|
||||
initialize();
|
||||
string handle = AmSession::getNewId();
|
||||
subscriptions[handle] = new AmSipSubscription(handle, info, sess_link);
|
||||
subscriptions[handle]->setExpiresInterval(wanted_expires);
|
||||
AmEventDispatcher::instance()->addEventQueue(handle, this);
|
||||
if (!subscriptions[handle]->doSubscribe()) {
|
||||
DBG("subscribe failed - removing subscription\b");
|
||||
AmEventDispatcher::instance()->delEventQueue(handle);
|
||||
delete subscriptions[handle];
|
||||
subscriptions.erase(handle);
|
||||
return "";
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
bool _AmSipSubscriptionContainer::refreshSubscription(const string& sub_handle,
|
||||
unsigned int wanted_expires) {
|
||||
bool res = true;
|
||||
subscriptions_mut.lock();
|
||||
AmSipSubscriptionMapIter it = subscriptions.find(sub_handle);
|
||||
if (it != subscriptions.end()) {
|
||||
DBG("refreshing subscription '%s'\n", sub_handle.c_str());
|
||||
it->second->setExpiresInterval(wanted_expires);
|
||||
res = it->second->reSubscribe();
|
||||
} else {
|
||||
DBG("subscription '%s' already removed\n", sub_handle.c_str());
|
||||
res = false;
|
||||
}
|
||||
subscriptions_mut.unlock();
|
||||
return res;
|
||||
}
|
||||
|
||||
void _AmSipSubscriptionContainer::removeSubscription(const string& sub_handle) {
|
||||
subscriptions_mut.lock();
|
||||
AmSipSubscriptionMapIter it = subscriptions.find(sub_handle);
|
||||
if (it != subscriptions.end()) {
|
||||
DBG("unsubscribing subscription '%s'\n", sub_handle.c_str());
|
||||
it->second->doUnsubscribe();
|
||||
} else {
|
||||
DBG("subscription '%s' already removed - ignoring\n", sub_handle.c_str());
|
||||
}
|
||||
subscriptions_mut.unlock();
|
||||
}
|
||||
|
||||
// AmEventQueueInterface
|
||||
void _AmSipSubscriptionContainer::postEvent(AmEvent* event) {
|
||||
auto_ptr<AmEvent> l_event(event);
|
||||
|
||||
AmSipRequestEvent* sip_req_ev = dynamic_cast<AmSipRequestEvent*>(event);
|
||||
if (sip_req_ev) {
|
||||
// DBG("got SIP request: '%s'\n", sip_req_ev->req.print().c_str());
|
||||
DBG("got SIP request: %s %s\n",
|
||||
sip_req_ev->req.method.c_str(), sip_req_ev->req.r_uri.c_str());
|
||||
string ltag = sip_req_ev->req.to_tag;
|
||||
|
||||
subscriptions_mut.lock();
|
||||
AmSipSubscriptionMapIter it = subscriptions.find(ltag);
|
||||
if (it == subscriptions.end()) {
|
||||
subscriptions_mut.unlock();
|
||||
WARN("got SIP request '%s' for unknown subscription '%s'\n",
|
||||
sip_req_ev->req.print().c_str(), ltag.c_str());
|
||||
AmSipDialog::reply_error(sip_req_ev->req, 481, SIP_REPLY_NOT_EXIST);
|
||||
return;
|
||||
}
|
||||
it->second->onRxRequest(sip_req_ev->req);
|
||||
if (it->second->getState() == AmSipSubscription::SipSubStateTerminated) {
|
||||
DBG("subscription '%s' terminated - removing\n", it->second->getDescription().c_str());
|
||||
delete it->second;
|
||||
subscriptions.erase(it);
|
||||
}
|
||||
subscriptions_mut.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
AmSipReplyEvent* sip_reply_ev = dynamic_cast<AmSipReplyEvent*>(event);
|
||||
if (sip_reply_ev) {
|
||||
DBG("got SIP reply: '%s'\n", sip_reply_ev->reply.print().c_str());
|
||||
string ltag =
|
||||
#ifdef HAVE_SEMS_OA
|
||||
sip_reply_ev->reply.from_tag;
|
||||
#else
|
||||
sip_reply_ev->reply.local_tag;
|
||||
#endif
|
||||
subscriptions_mut.lock();
|
||||
AmSipSubscriptionMapIter it = subscriptions.find(ltag);
|
||||
if (it == subscriptions.end()) {
|
||||
subscriptions_mut.unlock();
|
||||
WARN("got SIP reply '%s' for unknown subscription '%s'\n",
|
||||
sip_reply_ev->reply.print().c_str(), ltag.c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
it->second->onRxReply(sip_reply_ev->reply);
|
||||
if (it->second->getState() == AmSipSubscription::SipSubStateTerminated) {
|
||||
DBG("subscription '%s' terminated - removing\n", it->second->getDescription().c_str());
|
||||
delete it->second;
|
||||
subscriptions.erase(it);
|
||||
}
|
||||
subscriptions_mut.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2012 FRAFOS GmbH
|
||||
*
|
||||
* Development sponsored by Sipwise GmbH.
|
||||
*
|
||||
* This file is part of SEMS, a free SIP media server.
|
||||
*
|
||||
* SEMS 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 released under
|
||||
* the GPL with the additional exemption that compiling, linking,
|
||||
* and/or using OpenSSL is allowed.
|
||||
*
|
||||
* For a license to use the SEMS software under conditions
|
||||
* other than those described here, or to purchase support for this
|
||||
* software, please contact iptel.org by e-mail at the following addresses:
|
||||
* info@iptel.org
|
||||
*
|
||||
* SEMS 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
|
||||
*/
|
||||
#ifndef _AmSipSubscriptionContainer_h_
|
||||
#define _AmSipSubscriptionContainer_h_
|
||||
|
||||
#include "AmSipSubscription.h"
|
||||
#include "AmEventQueue.h"
|
||||
#include "AmThread.h"
|
||||
|
||||
class _AmSipSubscriptionContainer
|
||||
: public AmEventQueueInterface
|
||||
{
|
||||
AmSipSubscriptionMap subscriptions;
|
||||
AmMutex subscriptions_mut;
|
||||
|
||||
void initialize();
|
||||
bool initialized;
|
||||
public:
|
||||
_AmSipSubscriptionContainer();
|
||||
~_AmSipSubscriptionContainer();
|
||||
|
||||
string createSubscription(const AmSipSubscriptionInfo& info, const string& sess_link,
|
||||
unsigned int wanted_expires=0);
|
||||
bool refreshSubscription(const string& sub_handle, unsigned int wanted_expires=0);
|
||||
void removeSubscription(const string& sub_handle);
|
||||
|
||||
// AmEventQueueInterface
|
||||
void postEvent(AmEvent* event);
|
||||
};
|
||||
|
||||
typedef singleton<_AmSipSubscriptionContainer> AmSipSubscriptionContainer;
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,43 @@
|
||||
SIP Specific Event Notification (RFC3265) - support for DSM
|
||||
|
||||
subscription.create(info_struct)
|
||||
create new subscription. Parameters:
|
||||
$info_struct.domain - SIP domain (RURI, From, To)
|
||||
$info_struct.user - user (RURI, To)
|
||||
$info_struct.from_user - user (From)
|
||||
$info_struct.event - Event package (e.g. "reg")
|
||||
|
||||
$info_struct.pwd - optional: password (for SIP auth)
|
||||
$info_struct.proxy - optional: proxy URI (e.g. sip:10.0.0.1)
|
||||
$info_struct.accept - optional: Accept content
|
||||
$info_struct.id - optional: id (subscription ID to be used)
|
||||
$info_struct.expires - optional: desired subscription expiration
|
||||
|
||||
returns:
|
||||
$info_struct.handle - handle for subscription (sub dialog ltag)
|
||||
|
||||
subscription.refresh(handle [, expires])
|
||||
refresh subscription
|
||||
|
||||
subscription.remove(handle)
|
||||
unsubscribe & remove subscription
|
||||
|
||||
|
||||
|
||||
Example:
|
||||
set($r.domain="192.168.5.110");
|
||||
set($r.user="300");
|
||||
set($r.from_user="200");
|
||||
set($r.pwd="verysecret");
|
||||
-- set($r.proxy="sip:192.168.5.110");
|
||||
set($r.event="reg");
|
||||
-- set($r.id="115");
|
||||
-- set($r.expires="50");
|
||||
subscription.create(r);
|
||||
|
||||
if test($r.handle!="") {
|
||||
log(1, $r.handle);
|
||||
} else {
|
||||
log(1, "subscription failed:");
|
||||
logVars();
|
||||
}
|
||||
Loading…
Reference in new issue