apps/dsm/mods/mod_sbc/ModSbc.cpp
95beb393
 /*
  * 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 "ModSbc.h"
 #include "log.h"
 #include "AmUtils.h"
 
 #include "DSMCoreModule.h"
 
 #include "SBCCallProfile.h"
 #include "SBCDSMParams.h"
 #include "SBCCallLeg.h"
 
 SC_EXPORT(MOD_CLS_NAME);
 
 int MOD_CLS_NAME::preload() {
   DBG("initializing mod_sbc...\n");
   return 0;
 }
 
 MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) {
   DEF_CMD("sbc.profileSet", MODSBCActionProfileSet);
 
   DEF_CMD("sbc.stopCall", MODSBCActionStopCall);
   DEF_CMD("sbc.disconnect", MODSBCActionDisconnect);
   DEF_CMD("sbc.putOnHold", MODSBCActionPutOnHold);
   DEF_CMD("sbc.resumeHeld", MODSBCActionResumeHeld);
ac073b5d
   DEF_CMD("sbc.sendDisconnectEvent", MODSBCActionSendDisconnectEvent);
95beb393
 
fe26c1d5
   DEF_CMD("sbc.getCallStatus", MODSBCActionGetCallStatus);
94d0c4df
   DEF_CMD("sbc.relayReliableEvent", MODSBCActionB2BRelayReliable);
95beb393
 
ac073b5d
   DEF_CMD("sbc.addCallee", MODSBCActionAddCallee);
 
a947e6cc
   DEF_CMD("sbc.enableRelayDTMFReceiving", MODSBCEnableRelayDTMFReceiving);
   DEF_CMD("sbc.addToMediaProcessor", MODSBCAddToMediaProcessor);
   DEF_CMD("sbc.removeFromMediaProcessor", MODSBCRemoveFromMediaProcessor);
a1bad2c3
 
31420478
   DEF_CMD("sbc.streamsSetReceiving", MODSBCRtpStreamsSetReceiving);
a1bad2c3
 
95beb393
 } MOD_ACTIONEXPORT_END;
 
 MOD_CONDITIONEXPORT_BEGIN(MOD_CLS_NAME) {
   if (cmd == "legStateChange")
     return new TestDSMCondition(params, DSMCondition::LegStateChange);
 
   if (cmd == "bLegRefused")
     return new TestDSMCondition(params, DSMCondition::BLegRefused);
 
   // HOLD related
   if (cmd == "PutOnHold")
     return new TestDSMCondition(params, DSMCondition::PutOnHold);
 
   if (cmd == "ResumeHeld")
     return new TestDSMCondition(params, DSMCondition::ResumeHeld);
 
   if (cmd == "CreateHoldRequest")
     return new TestDSMCondition(params, DSMCondition::CreateHoldRequest);
 
   if (cmd == "HandleHoldReply")
     return new TestDSMCondition(params, DSMCondition::HandleHoldReply);
 
   // simple relay related
   if (cmd == "RelayInit")
     return new TestDSMCondition(params, DSMCondition::RelayInit);
 
   if (cmd == "RelayInitUAC")
     return new TestDSMCondition(params, DSMCondition::RelayInitUAC);
 
   if (cmd == "RelayInitUAS")
     return new TestDSMCondition(params, DSMCondition::RelayInitUAS);
 
   if (cmd == "RelayFinalize")
     return new TestDSMCondition(params, DSMCondition::RelayFinalize);
 
   if (cmd == "RelayOnSipRequest")
     return new TestDSMCondition(params, DSMCondition::RelayOnSipRequest);
 
   if (cmd == "RelayOnSipReply")
     return new TestDSMCondition(params, DSMCondition::RelayOnSipReply);
 
   if (cmd == "RelayOnB2BRequest")
     return new TestDSMCondition(params, DSMCondition::RelayOnB2BRequest);
 
   if (cmd == "RelayOnB2BReply")
     return new TestDSMCondition(params, DSMCondition::RelayOnB2BReply);
 
   if (cmd == "RelayOnB2BReply")
     return new TestDSMCondition(params, DSMCondition::RelayOnB2BReply);
 
   if (cmd == "sbc.isALeg")
     return new SBCIsALegCondition(params, false);
 
   if (cmd == "sbc.isOnHold")
     return new SBCIsOnHoldCondition(params, false);
 
7c772cd2
   if (cmd == "sbc.isDisconnected")
     return new SBCIsDisconnectedCondition(params, false);
 
   if (cmd == "sbc.isNoReply")
     return new SBCIsNoReplyCondition(params, false);
 
   if (cmd == "sbc.isRinging")
     return new SBCIsRingingCondition(params, false);
 
   if (cmd == "sbc.isConnected")
     return new SBCIsConnectedCondition(params, false);
 
   if (cmd == "sbc.isDisconnecting")
     return new SBCIsDisconnectingCondition(params, false);
 
 
95beb393
 } MOD_CONDITIONEXPORT_END
 
 
 MATCH_CONDITION_START(SBCIsALegCondition) {
   SBCCallLeg* call_leg = dynamic_cast<SBCCallLeg*>(sess);
   if (NULL == call_leg) {
     DBG("script writer error: DSM condition sbc.isALeg"
 	" used without call leg\n");
     return false;
   }
 
   bool b = call_leg->isALeg();
   bool res = inv ^ b;
   DBG("SBC: isALeg() == %s (res = %s)\n",
       b ? "true":"false", res ? "true":"false");
   return res;
 } MATCH_CONDITION_END;
 
 MATCH_CONDITION_START(SBCIsOnHoldCondition) {
   SBCCallLeg* call_leg = dynamic_cast<SBCCallLeg*>(sess);
   if (NULL == call_leg) {
     DBG("script writer error: DSM condition sbc.isOnHold"
 	" used without call leg\n");
     return false;
   }
 
   bool b = call_leg->isOnHold();
   bool res = inv ^ b;
   DBG("SBC: isOnHold() == %s (res = %s)\n",
       b ? "true":"false", res ? "true":"false");
   return res;
 } MATCH_CONDITION_END;
 
7c772cd2
 #define DEF_CALLSTATUS_COND(cond_name, cond_desc, call_status)		\
 									\
   MATCH_CONDITION_START(cond_name) {					\
     SBCCallLeg* call_leg = dynamic_cast<SBCCallLeg*>(sess);		\
     if (NULL == call_leg) {						\
       DBG("script writer error: DSM condition used without call leg\n"); \
       return false;							\
     }									\
 									\
     bool b = call_leg->getCallStatus() == call_status;			\
     bool res = inv ^ b;							\
     DBG("SBC: " cond_desc " == %s (res = %s)\n",			\
 	b ? "true":"false", res ? "true":"false");			\
     return res;								\
   } MATCH_CONDITION_END
 
 
 DEF_CALLSTATUS_COND(SBCIsDisconnectedCondition, "sbc.isDisconnected", CallLeg::Disconnected);
 DEF_CALLSTATUS_COND(SBCIsNoReplyCondition, "sbc.isNoReply", CallLeg::NoReply);
 DEF_CALLSTATUS_COND(SBCIsRingingCondition, "sbc.isRinging", CallLeg::Ringing);
 DEF_CALLSTATUS_COND(SBCIsConnectedCondition, "sbc.isConnected", CallLeg::Connected);
 DEF_CALLSTATUS_COND(SBCIsDisconnectingCondition, "sbc.isDisconnecting", CallLeg::Disconnecting);
95beb393
 
 #define ACTION_GET_PROFILE			\
   SBCCallProfile* profile = NULL;					\
   AVarMapT::iterator it = sc_sess->avar.find(DSM_SBC_AVAR_PROFILE);	\
   if (it != sc_sess->avar.end()) {					\
     profile = dynamic_cast<SBCCallProfile*>(it->second.asObject());	\
   }									\
   if (NULL == profile) {						\
   SBCCallLeg* call = dynamic_cast<SBCCallLeg*>(sess);			\
   if (NULL !=  call)							\
     profile = &call->getCallProfile();					\
   }									\
 									\
   if (NULL == profile) {						\
     ERROR("internal: Call profile object not found\n");			\
     EXEC_ACTION_STOP;							\
   }
 
 CONST_ACTION_2P(MODSBCActionProfileSet, ',', false);
 EXEC_ACTION_START(MODSBCActionProfileSet) {
   string profile_param = resolveVars(par1, sess, sc_sess, event_params);
   string value = resolveVars(par2, sess, sc_sess, event_params);
 
   ACTION_GET_PROFILE;
 
 #define SET_TO_CALL_PROFILE(cfgparam, member)		\
   if (profile_param == cfgparam) {			\
     profile->member=value;				\
     DBG(cfgparam " set to '%s'\n", value.c_str());	\
     EXEC_ACTION_STOP;					\
   }
 
 #define SET_TO_CALL_PROFILE_OPTION(cfgparam, member)			\
   if (profile_param == cfgparam) {					\
     profile->member=(value == "true");					\
     DBG(cfgparam " set to '%s'\n", profile->member?"true":"false");	\
     EXEC_ACTION_STOP;							\
   }
 
   switch (profile_param.length()) {
   case 2: 
   SET_TO_CALL_PROFILE("To", to);
   break;
 
   case 4:
   SET_TO_CALL_PROFILE("RURI", ruri);
   SET_TO_CALL_PROFILE("From", from);
   break;
 
 
   case 7:
   SET_TO_CALL_PROFILE("Call-ID", callid);
   break;
 
   case 8:
   SET_TO_CALL_PROFILE("next_hop", next_hop);
   break;
 
   case 9:
   SET_TO_CALL_PROFILE("RURI_host", ruri_host);
   break;
 
 
   case 11:
   SET_TO_CALL_PROFILE("refuse_with", refuse_with);
   break;
 
   default:
 
   SET_TO_CALL_PROFILE("outbound_proxy", outbound_proxy);
   SET_TO_CALL_PROFILE_OPTION("force_outbound_proxy", force_outbound_proxy);
 
   SET_TO_CALL_PROFILE("aleg_outbound_proxy", aleg_outbound_proxy);
   SET_TO_CALL_PROFILE_OPTION("aleg_force_outbound_proxy", aleg_force_outbound_proxy);
 
   SET_TO_CALL_PROFILE_OPTION("next_hop_1st_req", next_hop_1st_req);
   SET_TO_CALL_PROFILE_OPTION("patch_ruri_next_hop", patch_ruri_next_hop);
 
   SET_TO_CALL_PROFILE("aleg_next_hop", aleg_next_hop);
 
   // TODO: message_filter
   // TODO: header_filter
   // TODO: sdp_filter
 
   // TODO: auth
   // TODO: aleg_auth
 
   SET_TO_CALL_PROFILE("append_headers", append_headers);
   SET_TO_CALL_PROFILE("append_headers_req", append_headers_req);
   SET_TO_CALL_PROFILE("aleg_append_headers_req", aleg_append_headers_req);
 
5bb34f13
   SET_TO_CALL_PROFILE("rtprelay_enabled", rtprelay_enabled);
95beb393
 
   SET_TO_CALL_PROFILE_OPTION("force_symmetric_rtp", force_symmetric_rtp_value);
   SET_TO_CALL_PROFILE_OPTION("aleg_force_symmetric_rtp", aleg_force_symmetric_rtp_value);
   SET_TO_CALL_PROFILE_OPTION("msgflags_symmetric_rtp", msgflags_symmetric_rtp);
   SET_TO_CALL_PROFILE_OPTION("rtprelay_transparent_seqno", rtprelay_transparent_seqno);
   SET_TO_CALL_PROFILE_OPTION("rtprelay_transparent_ssrc", rtprelay_transparent_ssrc);
 
a947e6cc
   SET_TO_CALL_PROFILE_OPTION("rtprelay_dtmf_filtering", rtprelay_dtmf_filtering);
   SET_TO_CALL_PROFILE_OPTION("rtprelay_dtmf_detection", rtprelay_dtmf_detection);
 
95beb393
   if (profile_param == "rtprelay_interface") {
     profile->rtprelay_interface=value;
     if (!profile->evaluateRTPRelayInterface()) {
       sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
       sc_sess->SET_STRERROR("rtprelay_interface '"+value+"' not present");
     } else {
       DBG("rtprelay_interface set to '%s'\n", value.c_str());
     }
     EXEC_ACTION_STOP;
   }
 
   if (profile_param == "aleg_rtprelay_interface") {
     profile->aleg_rtprelay_interface=value;
     if (!profile->evaluateRTPRelayAlegInterface()) {
       sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
       sc_sess->SET_STRERROR("aleg_rtprelay_interface '"+value+"' not present");
     } else {
       DBG("aleg_rtprelay_interface set to '%s'\n", value.c_str());
     }
     EXEC_ACTION_STOP;
   }
 
   }
 
   // TODO: Transcoder Settings
   // TODO: CODEC Prefs
 
   // TODO: contact hiding
   // TODO: reg_caching
 
   DBG("script writer: Call profile property '%s' not known\n", profile_param.c_str());
   sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
   sc_sess->SET_STRERROR("Call profile property '"+profile_param+"' not known");
 } EXEC_ACTION_END;
 
 
 #define GET_CALL_LEG(action)						\
   CallLeg* call_leg = dynamic_cast<CallLeg*>(sess);			\
   if (NULL == call_leg) {						\
     DBG("script writer error: DSM action " #action			\
 	" used without call leg\n");					\
     throw DSMException("sbc", "type", "param", "cause",			\
 		       "script writer error: DSM action " #action	\
 		       " used without call leg");			\
   }
 
a1bad2c3
 #define GET_SBC_CALL_LEG(action)					\
   SBCCallLeg* sbc_call_leg = dynamic_cast<SBCCallLeg*>(sess);		\
   if (NULL == sbc_call_leg) {						\
     DBG("script writer error: DSM action " #action			\
 	" used without sbc call leg\n");				\
     throw DSMException("sbc", "type", "param", "cause",			\
 		       "script writer error: DSM action " #action	\
 		       " used without sbc call leg");			\
   }
 
 #define GET_B2B_MEDIA							\
   AmB2BMedia* b2b_media = sbc_call_leg->getMediaSession();		\
   DBG("session: %p, media: %p\n", sbc_call_leg, b2b_media);		\
   if (NULL == b2b_media) {						\
     DBG("No B2BMedia in current SBC call leg, sorry\n");		\
     sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);				\
     sc_sess->SET_STRERROR("No B2BMedia in current SBC call leg, sorry"); \
     EXEC_ACTION_STOP;							\
   }
 
95beb393
 EXEC_ACTION_START(MODSBCActionStopCall) {
   GET_CALL_LEG(StopCall);
   string cause = resolveVars(arg, sess, sc_sess, event_params);
   call_leg->stopCall(cause.c_str());
 } EXEC_ACTION_END;
 
2044fd3d
 CONST_ACTION_2P(MODSBCActionDisconnect, ',', true);
95beb393
 EXEC_ACTION_START(MODSBCActionDisconnect) {
   GET_CALL_LEG(Disconnect);
2044fd3d
   string hold_remote = resolveVars(par1, sess, sc_sess, event_params);
   string preserve_media_session = resolveVars(par2, sess, sc_sess, event_params);
   call_leg->disconnect(hold_remote == DSM_TRUE, preserve_media_session == DSM_TRUE);
95beb393
 } EXEC_ACTION_END;
 
ac073b5d
 EXEC_ACTION_START(MODSBCActionSendDisconnectEvent) {
   GET_CALL_LEG(SendDisconnectEvent);
   string hold_remote = resolveVars(arg, sess, sc_sess, event_params);
   if (!AmSessionContainer::instance()->postEvent(call_leg->getLocalTag(),
 						 new DisconnectLegEvent(hold_remote == DSM_TRUE))) {
     ERROR("couldn't self-post event\n");
   }
 } EXEC_ACTION_END;
 
95beb393
 
 EXEC_ACTION_START(MODSBCActionPutOnHold) {
   GET_CALL_LEG(PutOnHold);
   call_leg->putOnHold();
 } EXEC_ACTION_END;
 
 EXEC_ACTION_START(MODSBCActionResumeHeld) {
   GET_CALL_LEG(ResumeHeld);
2044fd3d
   call_leg->resumeHeld();
95beb393
 } EXEC_ACTION_END;
 
fe26c1d5
 EXEC_ACTION_START(MODSBCActionGetCallStatus) {
   GET_CALL_LEG(GetCallStatus);
c0cf6ec2
   string varname = arg;
   if (varname.size() && varname[0] == '$')
     varname.erase(0, 1);
fe26c1d5
   sc_sess->var[varname] = call_leg->getCallStatusStr();
   DBG("set $%s='%s'\n", varname.c_str(), sc_sess->var[varname].c_str());
 } EXEC_ACTION_END;
94d0c4df
 
 void setReliableEventParameters(const DSMSession* sc_sess, const string& var, VarMapT& params) {
   vector<string> vars = explode(var, ";");
   for (vector<string>::iterator it = vars.begin(); it != vars.end(); it++) {
     string varname = *it;
 
     if (varname.length() && varname[varname.length()-1]=='.') {
       DBG("adding postEvent param %s (struct)\n", varname.c_str());
 
       map<string, string>::const_iterator lb = sc_sess->var.lower_bound(varname);
       while (lb != sc_sess->var.end()) {
 	if ((lb->first.length() < varname.length()) ||
 	    strncmp(lb->first.c_str(), varname.c_str(), varname.length()))
 	  break;
 	params[lb->first] = lb->second;
 	lb++;
       }
     } else {
       VarMapT::const_iterator v_it = sc_sess->var.find(varname);
       if (v_it != sc_sess->var.end()) {
 	DBG("adding reliableEvent param %s=%s\n",
 	    it->c_str(), v_it->second.c_str());
 	params[varname] = v_it->second;
       }
     }
   }
 }
 
 CONST_ACTION_2P(MODSBCActionB2BRelayReliable, ',', false);
 EXEC_ACTION_START(MODSBCActionB2BRelayReliable) {
   GET_CALL_LEG(B2BRelayReliable);
   string ev_params = par1;
   vector<string> success_params = explode(par2, ","); // q&d...
   B2BEvent* processed = new B2BEvent(E_B2B_APP, B2BEvent::B2BApplication);
   if (success_params.size()) {
     setReliableEventParameters(sc_sess, trim(success_params[0], " "), processed->params);
   }
   B2BEvent* unprocessed = new B2BEvent(E_B2B_APP, B2BEvent::B2BApplication);
   if (success_params.size() > 1) {
     DBG("p='%s'\n", success_params[1].c_str());
     setReliableEventParameters(sc_sess, trim(success_params[1], " "), unprocessed->params);
   }
 
   ReliableB2BEvent* rel_b2b_ev = new ReliableB2BEvent(E_B2B_APP, B2BEvent::B2BApplication, processed, unprocessed);
   setReliableEventParameters(sc_sess, ev_params, rel_b2b_ev->params);
 
   rel_b2b_ev->setSender(call_leg->getLocalTag());
   call_leg->relayEvent(rel_b2b_ev);
 } EXEC_ACTION_END;
ac073b5d
 
 CONST_ACTION_2P(MODSBCActionAddCallee, ',', false);
 EXEC_ACTION_START(MODSBCActionAddCallee) {
a1bad2c3
   GET_SBC_CALL_LEG(sbc.addCallee);
ac073b5d
 
   string mode = resolveVars(par1, sess, sc_sess, event_params);
9bce0457
   string varname = par2;
ac073b5d
 
   if (mode == DSM_SBC_PARAM_ADDCALLEE_MODE_STR) {
     string hdrs;
0ac38ad4
 
     VarMapT::iterator it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_TRANSPARENT_DLG_ID);
     if (it != sc_sess->var.end()) {
       sbc_call_leg->getCallProfile().transparent_dlg_id = it->second == DSM_TRUE;
     } else {
       // default to false
       sbc_call_leg->getCallProfile().transparent_dlg_id = false;
     }
 
       DBG("Using %stransparent dialog IDs for new call leg\n",
 	  sbc_call_leg->getCallProfile().transparent_dlg_id ? "":"non-");
 
ac073b5d
     SBCCallLeg* peer = new SBCCallLeg(sbc_call_leg);
0ac38ad4
 
ac073b5d
     SBCCallProfile &p = peer->getCallProfile();
a1bad2c3
     AmB2BSession::RTPRelayMode rtp_mode = sbc_call_leg->getRtpRelayMode();
ac073b5d
 
0ac38ad4
     it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_LOCAL_PARTY);
ac073b5d
     if (it != sc_sess->var.end())
       peer->setLocalParty(it->second, it->second);
 
     it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_REMOTE_PARTY);
     if (it != sc_sess->var.end())
       peer->setRemoteParty(it->second, it->second);
 
     it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_HDRS);
     if (it != sc_sess->var.end())
       hdrs = it->second;
 
     it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_OUTBOUND_PROXY);
     if (it != sc_sess->var.end())
       p.outbound_proxy = it->second;
 
43aab77c
     it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_NEXT_HOP);
     if (it != sc_sess->var.end())
       p.next_hop = it->second;
 
     it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_NEXT_HOP_1ST_REQ);
     if (it != sc_sess->var.end())
       p.next_hop_1st_req = (it->second == DSM_TRUE);
 
     it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_NEXT_HOP_PATCH_RURI);
     if (it != sc_sess->var.end())
       p.patch_ruri_next_hop = (it->second == DSM_TRUE);
 
     it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_NEXT_HOP_FIXED);
     if (it != sc_sess->var.end())
       p.next_hop_fixed = (it->second == DSM_TRUE);
 
49e592d1
     it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_OUTBOUND_INTERFACE);
     if (it != sc_sess->var.end()) {
       p.outbound_interface = it->second;
       p.evaluateOutboundInterface();
     }
 
6940a6e3
     it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_RTP_INTERFACE);
     if (it != sc_sess->var.end()) {
       p.rtprelay_interface = it->second;
       p.evaluateRTPRelayInterface();
     }
 
2044fd3d
     sbc_call_leg->addCallee(peer, hdrs);
9bce0457
   } else if (mode == DSM_SBC_PARAM_ADDCALLEE_MODE_LTAG) {
     string ltag;
     string hdrs;
 
     VarMapT::iterator it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_LTAG);
     if (it != sc_sess->var.end())
       ltag = it->second;
 
     it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_HDRS);
     if (it != sc_sess->var.end())
       hdrs = it->second;
 
     sbc_call_leg->addCallee(ltag, hdrs);
ac073b5d
   }
 
 } EXEC_ACTION_END;
a947e6cc
 
 EXEC_ACTION_START(MODSBCEnableRelayDTMFReceiving) {
   bool enable = (resolveVars(arg, sess, sc_sess, event_params)==DSM_TRUE);
 
a1bad2c3
   GET_SBC_CALL_LEG(AddCallee);
   GET_B2B_MEDIA;
a947e6cc
 
a1bad2c3
   b2b_media->setRelayDTMFReceiving(enable);
a947e6cc
 } EXEC_ACTION_END;
 
 EXEC_ACTION_START(MODSBCAddToMediaProcessor) {
   GET_CALL_LEG(AddToMediaProcessor);
   AmMediaProcessor::instance()->addSession(call_leg, call_leg->getCallgroup());
 
 } EXEC_ACTION_END;
 
 EXEC_ACTION_START(MODSBCRemoveFromMediaProcessor) {
   GET_CALL_LEG(RemoveFromMediaProcessor);
   AmMediaProcessor::instance()->removeSession(call_leg);
 } EXEC_ACTION_END;
a1bad2c3
 
31420478
 CONST_ACTION_2P(MODSBCRtpStreamsSetReceiving, ',', false);
 EXEC_ACTION_START(MODSBCRtpStreamsSetReceiving) {
a1bad2c3
   bool p_a = (resolveVars(par1, sess, sc_sess, event_params)==DSM_TRUE);
   bool p_b = (resolveVars(par2, sess, sc_sess, event_params)==DSM_TRUE);
 
31420478
   GET_SBC_CALL_LEG(RtpStreamsSetReceiving);
a1bad2c3
   GET_B2B_MEDIA;
 
31420478
   b2b_media->setReceiving(p_a, p_b);
a1bad2c3
 } EXEC_ACTION_END;