You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sems/apps/sbc/call_control/dsm/CCDSM.cpp

286 lines
9.2 KiB

/*
* Copyright (C) 2013 Stefan Sayer
*
* 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 "log.h"
#include "CCDSM.h"
static const string data_var_name(MOD_NAME "::data");
EXPORT_PLUGIN_CLASS_FACTORY(CCDSMFactory, MOD_NAME);
CCDSMModule* CCDSMModule::_instance = 0;
CCDSMModule* CCDSMModule::instance()
{
if (!_instance) _instance = new CCDSMModule();
return _instance;
}
void CCDSMModule::invoke(const string& method, const AmArg& args, AmArg& ret)
{
DBG(MOD_NAME " %s(%s)\n", method.c_str(), AmArg::print(args).c_str());
if ((method == "start") || (method == "connect") || (method == "end")) {
return; // FIXME: advertise that the interface shouldn't be used
}
else if(method == "_list"){
ret.push("start");
ret.push("connect");
ret.push("end");
}
else if (method == "getExtendedInterfaceHandler") {
ret.push((AmObject*)this);
}
else
throw AmDynInvoke::NotImplemented(method);
}
SBCDSMInstance* CCDSMModule::getDSMInstance(SBCCallProfile &profile)
{
SBCVarMapIteratorT i = profile.cc_vars.find(data_var_name);
if (i != profile.cc_vars.end())
return dynamic_cast<SBCDSMInstance*>(i->second.asObject());
return NULL;
}
void CCDSMModule::deleteDSMInstance(SBCCallProfile &profile)
{
SBCVarMapIteratorT i = profile.cc_vars.find(data_var_name);
if (i != profile.cc_vars.end()) {
SBCDSMInstance* h = dynamic_cast<SBCDSMInstance*>(i->second.asObject());
if (h) delete h;
profile.cc_vars.erase(i);
}
}
void CCDSMModule::resetDSMInstance(SBCCallProfile &profile)
{
SBCVarMapIteratorT i = profile.cc_vars.find(data_var_name);
if (i != profile.cc_vars.end()) {
profile.cc_vars.erase(i);
}
}
bool CCDSMModule::init(SBCCallLeg *call, const map<string, string> &values)
{
DBG("ExtCC: init - call instance: '%p' isAleg==%s\n", call, call->isALeg()?"true":"false");
SBCCallProfile &profile = call->getCallProfile();
resetDSMInstance(profile); // just forget the handler if already set
// if (!call->isALeg()) return;
// create DSM handler if necessary (with evaluated configuration)
try {
SBCDSMInstance* h = new SBCDSMInstance(call, values);
profile.cc_vars[data_var_name] = (AmObject*)h;
} catch (const string& e) {
ERROR("initializing DSM Call control module: '%s'\n", e.c_str());
return false;
} catch (...) {
ERROR("initializing DSM Call control module\n");
return false;
}
return true;
}
#define GET_DSM_INSTANCE \
SBCDSMInstance* h = getDSMInstance(call->getCallProfile()); \
if (NULL == h) \
return StopProcessing;
#define GET_DSM_INSTANCE_VOID \
SBCDSMInstance* h = getDSMInstance(call->getCallProfile()); \
if (NULL == h) { \
ERROR("DSM instance not found for call leg\n"); \
return;/* todo: throw exception? */ \
}
CCChainProcessing CCDSMModule::onInitialInvite(SBCCallLeg *call, InitialInviteHandlerParams &params)
{
DBG("ExtCC: onInitialInvite - call instance: '%p' isAleg==%s\n", call, call->isALeg()?"true":"false");
GET_DSM_INSTANCE;
return h->onInitialInvite(call, params);
}
void CCDSMModule::onStateChange(SBCCallLeg *call, const CallLeg::StatusChangeCause &cause) {
DBG("ExtCC: onStateChange - call instance: '%p' isAleg==%s\n", call, call->isALeg()?"true":"false");
GET_DSM_INSTANCE_VOID;
h->onStateChange(call, cause);
};
CCChainProcessing CCDSMModule::onBLegRefused(SBCCallLeg *call, const AmSipReply& reply)
{
DBG("ExtCC: onBLegRefused - call instance: '%p' isAleg==%s\n", call, call->isALeg()?"true":"false");
GET_DSM_INSTANCE;
return h->onBLegRefused(call, reply);
}
void CCDSMModule::onDestroyLeg(SBCCallLeg *call) {
DBG("ExtCC: onDestroyLeg - call instance: '%p' isAleg==%s\n", call, call->isALeg()?"true":"false");
DBG("TODO: call DSM event?\n");
deleteDSMInstance(call->getCallProfile());
}
/** called from A/B leg when in-dialog request comes in */
CCChainProcessing CCDSMModule::onInDialogRequest(SBCCallLeg *call, const AmSipRequest &req) {
DBG("ExtCC: onInDialogRequest - call instance: '%p' isAleg==%s\n", call, call->isALeg()?"true":"false");
GET_DSM_INSTANCE;
return h->onInDialogRequest(call, req);
}
CCChainProcessing CCDSMModule::onInDialogReply(SBCCallLeg *call, const AmSipReply &reply) {
DBG("ExtCC: onInDialogReply - call instance: '%p' isAleg==%s\n", call, call->isALeg()?"true":"false");
GET_DSM_INSTANCE;
return h->onInDialogReply(call, reply);
}
/** called before any other processing for the event is done */
CCChainProcessing CCDSMModule::onEvent(SBCCallLeg *call, AmEvent *e) {
DBG("ExtCC: onEvent - call instance: '%p' isAleg==%s\n", call, call->isALeg()?"true":"false");
GET_DSM_INSTANCE;
return h->onEvent(call, e);
}
CCChainProcessing CCDSMModule::onDtmf(SBCCallLeg *call, int event, int duration) {
DBG("ExtCC: onDtmf(%i;%i) - call instance: '%p' isAleg==%s\n",
event, duration, call, call->isALeg()?"true":"false");
GET_DSM_INSTANCE;
return h->onDtmf(call, event, duration);
}
// hold related functionality
CCChainProcessing CCDSMModule::putOnHold(SBCCallLeg *call) {
DBG("ExtCC: putOnHold - call instance: '%p' isAleg==%s\n", call, call->isALeg()?"true":"false");
GET_DSM_INSTANCE;
return h->putOnHold(call);
}
CCChainProcessing CCDSMModule::resumeHeld(SBCCallLeg *call, bool send_reinvite) {
DBG("ExtCC: resumeHeld - call instance: '%p' isAleg==%s\n", call, call->isALeg()?"true":"false");
GET_DSM_INSTANCE;
return h->resumeHeld(call, send_reinvite);
}
CCChainProcessing CCDSMModule::createHoldRequest(SBCCallLeg *call, AmSdp &sdp) {
DBG("ExtCC: createHoldRequest - call instance: '%p' isAleg==%s\n", call, call->isALeg()?"true":"false");
GET_DSM_INSTANCE;
return h->createHoldRequest(call, sdp);
}
CCChainProcessing CCDSMModule::handleHoldReply(SBCCallLeg *call, bool succeeded) {
DBG("ExtCC: handleHoldReply - call instance: '%p' isAleg==%s\n", call, call->isALeg()?"true":"false");
GET_DSM_INSTANCE;
return h->handleHoldReply(call, succeeded);
}
/** Possibility to influence messages relayed to the B2B peer leg.
return value:
- lower than 0 means error (returned upstream, the one
returning error is responsible for destrying the event instance)
- greater than 0 means "stop processing and return 0 upstream"
- equal to 0 means "continue processing" */
int CCDSMModule::relayEvent(SBCCallLeg *call, AmEvent *e) {
// todo
return 0;
}
// using extended CC modules with simple relay
struct RelayUserData {
SimpleRelayDialog *relay;
SBCCallProfile& profile;
RelayUserData(SimpleRelayDialog *relay, SBCCallProfile& profile)
: relay(relay), profile(profile) { }
};
bool CCDSMModule::init(SBCCallProfile& profile, SimpleRelayDialog *relay, void *&user_data) {
SBCDSMInstance* h = getDSMInstance(profile);
if (NULL == h) {
user_data = NULL;
return false;
}
if (!h->init(profile, relay)) {
return false;
}
user_data = new RelayUserData(relay, profile);
return true;
}
#define GET_USER_DATA \
if (NULL == user_data) \
return; \
\
RelayUserData* ud= (RelayUserData*)user_data; \
SBCDSMInstance* h = getDSMInstance(ud->profile); \
if (NULL == h) { \
ERROR("SBC DSM instance disappeared, huh?\n"); \
return; \
}
void CCDSMModule::initUAC(const AmSipRequest &req, void *user_data) {
GET_USER_DATA;
h->initUAC(ud->profile, ud->relay, req);
}
void CCDSMModule::initUAS(const AmSipRequest &req, void *user_data) {
GET_USER_DATA;
h->initUAS(ud->profile, ud->relay, req);
}
void CCDSMModule::finalize(void *user_data) {
GET_USER_DATA;
h->finalize(ud->profile, ud->relay);
delete ud;
}
void CCDSMModule::onSipRequest(const AmSipRequest& req, void *user_data) {
GET_USER_DATA;
h->onSipRequest(ud->profile, ud->relay, req);
}
void CCDSMModule::onSipReply(const AmSipRequest& req,
const AmSipReply& reply,
AmBasicSipDialog::Status old_dlg_status,
void *user_data) {
GET_USER_DATA;
h->onSipReply(ud->profile, ud->relay, req, reply, old_dlg_status);
}
void CCDSMModule::onB2BRequest(const AmSipRequest& req, void *user_data) {
GET_USER_DATA;
h->onB2BRequest(ud->profile, ud->relay, req);
}
void CCDSMModule::onB2BReply(const AmSipReply& reply, void *user_data) {
GET_USER_DATA;
h->onB2BReply(ud->profile, ud->relay, reply);
}