/* * 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(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(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 &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 ¶ms) { 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); }