RFC3265 Event notification (SUBSCRIBE/NOTIFY) support - SEMS as susbcriber

Development sponsored by Sipwise GmbH.

Conflicts:

	apps/dsm/DSMCall.cpp
	apps/dsm/DSMStateEngine.h
	core/sip/defs.h
sayer/1.4-spce2.6
Stefan Sayer 15 years ago
parent 1eb5be3f7b
commit d9795df0b8

@ -29,6 +29,7 @@
#include "AmUtils.h"
#include "AmMediaProcessor.h"
#include "DSM.h"
#include "AmSipSubscription.h"
#include "../apps/jsonrpc/JsonRPCEvents.h" // todo!
@ -440,6 +441,24 @@ void DSMCall::process(AmEvent* event)
}
if (event->event_id == E_SIP_SUBSCRIPTION) {
SIPSubscriptionEvent* sub_ev = dynamic_cast<SIPSubscriptionEvent*>(event);
if (sub_ev) {
DBG("DSM Call received SIP Subscription Event\n");
map<string, string> params;
params["status"] = sub_ev->getStatusText();
params["code"] = int2str(sub_ev->code);
params["reason"] = sub_ev->reason;
params["expires"] = int2str(sub_ev->expires);
params["has_body"] = (!sub_ev->notify_body.empty())?"true":"false";
if (!sub_ev->notify_body.empty()) {
avar[DSM_AVAR_SIPSUBSCRIPTION_BODY] = AmArg(sub_ev->notify_body);
}
engine.runEvent(this, this, DSMCondition::SIPSubscription, &params);
avar.erase(DSM_AVAR_SIPSUBSCRIPTION_BODY);
}
}
AmB2BCallerSession::process(event);
}

@ -197,6 +197,9 @@ DSMCondition* DSMCoreModule::getCondition(const string& from_str) {
if (cmd == "jsonRpcResponse")
return new TestDSMCondition(params, DSMCondition::JsonRpcResponse);
if (cmd == "subscription")
return new TestDSMCondition(params, DSMCondition::SIPSubscription);
if (cmd == "startup")
return new TestDSMCondition(params, DSMCondition::Startup);

@ -66,6 +66,8 @@ using std::map;
#define DSM_AVAR_JSONRPCRESPONSEDATA "JsonRpcResponseParameters"
#define DSM_AVAR_JSONRPCRESPONSEUDATA "JsonRpcResponseUData"
#define DSM_AVAR_SIPSUBSCRIPTION_BODY "SipSubscriptionBody"
#define DSM_ERRNO_FILE "file"
#define DSM_ERRNO_UNKNOWN_ARG "arg"
#define DSM_ERRNO_SCRIPT "script"

@ -90,7 +90,9 @@ class DSMCondition
Startup,
Reload,
System
System,
SIPSubscription
};
bool invert;

@ -6,6 +6,7 @@
#include "DSMStateDiagramCollection.h"
#include "../apps/jsonrpc/JsonRPCEvents.h" // todo!
#include "AmSipSubscription.h"
SystemDSM::SystemDSM(const DSMScriptConfig& config,
const string& startDiagName,
@ -127,10 +128,28 @@ void SystemDSM::process(AmEvent* event) {
}
if (event->event_id == E_SIP_SUBSCRIPTION) {
SIPSubscriptionEvent* sub_ev = dynamic_cast<SIPSubscriptionEvent*>(event);
if (sub_ev) {
DBG("SystemDSM received SIP Subscription Event\n");
map<string, string> params;
params["status"] = sub_ev->getStatusText();
params["code"] = int2str(sub_ev->code);
params["reason"] = sub_ev->reason;
params["expires"] = int2str(sub_ev->expires);
params["has_body"] = (!sub_ev->notify_body.empty())?"true":"false";
if (!sub_ev->notify_body.empty()) {
avar[DSM_AVAR_SIPSUBSCRIPTION_BODY] = AmArg(sub_ev->notify_body);
}
engine.runEvent(&dummy_session, this, DSMCondition::SIPSubscription, &params);
avar.erase(DSM_AVAR_SIPSUBSCRIPTION_BODY);
}
}
if (event->event_id == E_SYSTEM) {
AmSystemEvent* sys_ev = dynamic_cast<AmSystemEvent*>(event);
if(sys_ev){
DBG("SystemDSM received system Event\n");
DBG("SystemDSM received system Event\n");
map<string, string> params;
params["type"] = AmSystemEvent::getDescription(sys_ev->sys_event);
engine.runEvent(&dummy_session, this, DSMCondition::System, &params);

@ -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

@ -33,8 +33,9 @@
#include <string>
using std::string;
#define E_PLUGIN 100
#define E_SYSTEM 101
#define E_PLUGIN 100
#define E_SYSTEM 101
#define E_SIP_SUBSCRIPTION 102
/** \brief base event class */
struct AmEvent

@ -9,6 +9,7 @@
#define SIP_METH_UPDATE "UPDATE"
#define SIP_METH_BYE "BYE"
#define SIP_METH_ACK "ACK"
#define SIP_METH_SUBSCRIBE "SUBSCRIBE"
#define SIP_METH_NOTIFY "NOTIFY"
#define SIP_HDR_FROM "From"
@ -36,6 +37,9 @@
#define SIP_HDR_WWW_AUTHENTICATE "WWW-Authenticate"
#define SIP_HDR_ALLOW "Allow"
#define SIP_HDR_RETRY_AFTER "Retry-After"
#define SIP_HDR_ACCEPT "Accept"
#define SIP_HDR_EVENT "Event"
#define SIP_HDR_SUBSCRIPTION_STATE "Subscription-State"
#define SIP_HDR_COL(_hdr) _hdr ":"
#define SIP_HDR_COLSP(_hdr) SIP_HDR_COL(_hdr) " "

@ -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

@ -410,6 +410,12 @@ brackets match.
#params.* - parameters array
subscription - SIP subscription status
#status - status: "active", "pending", "failed", "terminated", "timeout"
#code - SIP response code (if failed)
#reason - SIP response reason (if failed)
NOTIFY body in avar[DSM_AVAR_SIPSUBSCRIPTION_BODY] (if #status=="active")
jsonRpcResponse - json-rpc response received
#ev_type - JsonRpcResponse
#id - response ID

@ -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…
Cancel
Save