apps/sbc/SBC.cpp
f86ac8e0
 /*
499d8dd3
  * Copyright (C) 2010-2011 Stefan Sayer
f86ac8e0
  *
  * 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.
  *
  * 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
  */
 
 /* 
 SBC - feature-wishlist
 - accounting (MySQL DB, cassandra DB)
 - RTP transcoding mode (bridging)
 - overload handling (parallel call to target thresholds)
 - call distribution
 - select profile on monitoring in-mem DB record
  */
 #include "SBC.h"
 
2f3af87c
 #include "SBCCallControlAPI.h"
dc527624
 
f86ac8e0
 #include "log.h"
 #include "AmUtils.h"
 #include "AmAudio.h"
 #include "AmPlugIn.h"
 #include "AmMediaProcessor.h"
 #include "AmConfigReader.h"
 #include "AmSessionContainer.h"
 #include "AmSipHeaders.h"
aadc4574
 #include "SBCSimpleRelay.h"
 #include "RegisterDialog.h"
 #include "SubscriptionDialog.h"
7ed026c3
 #include "sip/pcap_logger.h"
6f1c3543
 #include "sip/sip_parser.h"
 #include "sip/sip_trans.h"
f86ac8e0
 
 #include "HeaderFilter.h"
bb763d84
 #include "ParamReplacer.h"
4fb48b9e
 #include "SDPFilter.h"
2cfe4c66
 #include "SBCCallLeg.h"
01c4e1c7
 
 #include "AmEventQueueProcessor.h"
 
 #include "SubscriptionDialog.h"
 #include "RegisterDialog.h"
 #include "RegisterCache.h"
 
6d39e520
 #include <algorithm>
f86ac8e0
 
 using std::map;
 
bb5d7550
 #define MOD_NAME "sbc"
 
f86ac8e0
 
6337394b
 EXPORT_MODULE_FACTORY(SBCFactory);
 DEFINE_MODULE_INSTANCE(SBCFactory, MOD_NAME);
f86ac8e0
 
6d39e520
 // helper functions
 
4735758f
 bool getCCInterfaces(CCInterfaceListT& cc_interfaces, vector<AmDynInvoke*>& cc_modules)
aadc4574
 {
   for (CCInterfaceListIteratorT cc_it = cc_interfaces.begin();
        cc_it != cc_interfaces.end(); cc_it++) {
     string& cc_module = cc_it->cc_module;
     if (cc_module.empty()) {
       ERROR("using call control but empty cc_module for '%s'!\n", 
 	    cc_it->cc_name.c_str());
       return false;
     }
 
     AmDynInvokeFactory* cc_fact = AmPlugIn::instance()->getFactory4Di(cc_module);
     if (NULL == cc_fact) {
       ERROR("cc_module '%s' not loaded\n", cc_module.c_str());
       return false;
     }
 
     AmDynInvoke* cc_di = cc_fact->getInstance();
     if(NULL == cc_di) {
       ERROR("could not get a DI reference\n");
       return false;
     }
     cc_modules.push_back(cc_di);
   }
   return true;
 }
 
4735758f
 void assertEndCRLF(string& s) {
   if (s[s.size()-2] != '\r' ||
       s[s.size()-1] != '\n') {
     while ((s[s.size()-1] == '\r') ||
 	   (s[s.size()-1] == '\n'))
       s.erase(s.size()-1);
     s += "\r\n";
4f6c0cc5
   }
9e8a861b
 }
 
6d39e520
 ///////////////////////////////////////////////////////////////////////////////////////////
 
9fce821e
 SBCCallLeg* CallLegCreator::create(const SBCCallProfile& call_profile)
36a9d052
 {
9fce821e
   return new SBCCallLeg(call_profile, new AmSipDialog());
 }
 
 SBCCallLeg* CallLegCreator::create(SBCCallLeg* caller)
 {
   return new SBCCallLeg(caller);
36a9d052
 }
 
9f8ba5c7
 SimpleRelayCreator::Relay 
e59cd844
 SimpleRelayCreator::createRegisterRelay(SBCCallProfile& call_profile,
 					vector<AmDynInvoke*> &cc_modules)
 {
9f8ba5c7
   return SimpleRelayCreator::Relay(new RegisterDialog(call_profile, cc_modules),
 				   new RegisterDialog(call_profile, cc_modules));
e59cd844
 }
 
9f8ba5c7
 SimpleRelayCreator::Relay
e59cd844
 SimpleRelayCreator::createSubscriptionRelay(SBCCallProfile& call_profile,
 					    vector<AmDynInvoke*> &cc_modules)
 {
9f8ba5c7
   return SimpleRelayCreator::Relay(new SubscriptionDialog(call_profile, cc_modules),
 				   new SubscriptionDialog(call_profile, cc_modules));
e59cd844
 }
 
9f8ba5c7
 SimpleRelayCreator::Relay
e59cd844
 SimpleRelayCreator::createGenericRelay(SBCCallProfile& call_profile,
 				       vector<AmDynInvoke*> &cc_modules)
 {
9f8ba5c7
   return SimpleRelayCreator::Relay(new SimpleRelayDialog(call_profile, cc_modules),
 				   new SimpleRelayDialog(call_profile, cc_modules));
e59cd844
 }
 
f86ac8e0
 SBCFactory::SBCFactory(const string& _app_name)
9fce821e
   : AmSessionFactory(_app_name), 
     AmDynInvokeFactory(_app_name),
cf2e42c1
     core_options_handling(false),
e59cd844
     callLegCreator(new CallLegCreator()),
     simpleRelayCreator(new SimpleRelayCreator())
f86ac8e0
 {
 }
 
527d48f8
 SBCFactory::~SBCFactory() {
01c4e1c7
   RegisterCache::dispose();
527d48f8
 }
f86ac8e0
 
 int SBCFactory::onLoad()
 {
   if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) {
     ERROR("No configuration for sbc present (%s)\n",
 	 (AmConfig::ModConfigPath + string(MOD_NAME ".conf")).c_str()
 	 );
     return -1;
   }
 
e708b948
   string load_cc_plugins = cfg.getParameter("load_cc_plugins");
   if (!load_cc_plugins.empty()) {
     INFO("loading call control plugins '%s' from '%s'\n",
 	 load_cc_plugins.c_str(), AmConfig::PlugInPath.c_str());
     if (AmPlugIn::instance()->load(AmConfig::PlugInPath, load_cc_plugins) < 0) {
       ERROR("loading call control plugins '%s' from '%s'\n",
 	    load_cc_plugins.c_str(), AmConfig::PlugInPath.c_str());
       return -1;
     }
   }
 
f86ac8e0
   session_timer_fact = AmPlugIn::instance()->getFactory4Seh("session_timer");
   if(!session_timer_fact) {
da5cc058
     WARN("session_timer plug-in not loaded - "
 	 "SIP Session Timers will not be supported\n");
f86ac8e0
   }
 
   vector<string> profiles_names = explode(cfg.getParameter("profiles"), ",");
   for (vector<string>::iterator it =
 	 profiles_names.begin(); it != profiles_names.end(); it++) {
     string profile_file_name = AmConfig::ModConfigPath + *it + ".sbcprofile.conf";
     if (!call_profiles[*it].readFromConfiguration(*it, profile_file_name)) {
       ERROR("configuring SBC call profile from '%s'\n", profile_file_name.c_str());
       return -1;
     }
   }
 
c60b508e
   active_profile = explode(cfg.getParameter("active_profile"), ",");
   if (active_profile.empty()) {
     ERROR("active_profile not set.\n");
f86ac8e0
     return -1;
   }
 
c60b508e
   string active_profile_s;
   for (vector<string>::iterator it =
 	 active_profile.begin(); it != active_profile.end(); it++) {
     if (it->empty())
       continue;
     if (((*it)[0] != '$') && call_profiles.find(*it) == call_profiles.end()) {
       ERROR("call profile active_profile '%s' not loaded!\n", it->c_str());
       return -1;
     }
6bced481
     active_profile_s+=*it;
     if (it != active_profile.end()-1)
       active_profile_s+=", ";
c60b508e
   }
 
   INFO("SBC: active profile: '%s'\n", active_profile_s.c_str());
 
   vector<string> regex_maps = explode(cfg.getParameter("regex_maps"), ",");
   for (vector<string>::iterator it =
 	 regex_maps.begin(); it != regex_maps.end(); it++) {
     string regex_map_file_name = AmConfig::ModConfigPath + *it + ".conf";
     RegexMappingVector v;
     if (!read_regex_mapping(regex_map_file_name, "=>",
 			    ("SBC regex mapping " + *it+":").c_str(), v)) {
       ERROR("reading regex mapping from '%s'\n", regex_map_file_name.c_str());
       return -1;
     }
     regex_mappings.setRegexMap(*it, v);
     INFO("loaded regex mapping '%s'\n", it->c_str());
   }
6337394b
 
cf2e42c1
   core_options_handling = cfg.getParameter("core_options_handling") == "yes";
   DBG("OPTIONS messages handled by the core: %s\n", core_options_handling?"yes":"no");
 
6337394b
   if (!AmPlugIn::registerApplication(MOD_NAME, this)) {
     ERROR("registering "MOD_NAME" application\n");
     return -1;
   }
 
   if (!AmPlugIn::registerDIInterface(MOD_NAME, this)) {
     ERROR("registering "MOD_NAME" DI interface\n");
     return -1;
   }
 
d58fb144
   // TODO: add config param for the number of threads
   subnot_processor.addThreads(1);
01c4e1c7
   RegisterCache::instance()->start();
d58fb144
 
f86ac8e0
   return 0;
 }
 
c60b508e
 /** get the first matching profile name from active profiles */
aadc4574
 SBCCallProfile* SBCFactory::getActiveProfileMatch(const AmSipRequest& req,
 						  ParamReplacerCtx& ctx) 
 {
   string profile, profile_rule;
   vector<string>::const_iterator it = active_profile.begin();
   for (; it != active_profile.end(); it++) {
 
c60b508e
     if (it->empty())
       continue;
 
     if (*it == "$(paramhdr)")
aadc4574
       profile = get_header_keyvalue(ctx.app_param,"profile");
c60b508e
     else if (*it == "$(ruri.user)")
aadc4574
       profile = req.user;
c60b508e
     else
aadc4574
       profile = ctx.replaceParameters(*it, "active_profile", req);
c60b508e
 
aadc4574
     if (!profile.empty()) {
       profile_rule = *it;
c60b508e
       break;
     }
   }
f86ac8e0
 
aadc4574
   DBG("active profile = %s\n", profile.c_str());
527d48f8
 
aadc4574
   map<string, SBCCallProfile>::iterator prof_it = call_profiles.find(profile);
   if (prof_it==call_profiles.end()) {
     ERROR("could not find call profile '%s'"
 	  " (matching active_profile rule: '%s')\n",
c60b508e
 	  profile.c_str(), profile_rule.c_str());
aadc4574
 
     return NULL;
f86ac8e0
   }
 
c60b508e
   DBG("using call profile '%s' (from matching active_profile rule '%s')\n",
       profile.c_str(), profile_rule.c_str());
 
aadc4574
   return &prof_it->second;
 }
c60b508e
 
aadc4574
 AmSession* SBCFactory::onInvite(const AmSipRequest& req, const string& app_name,
 				const map<string,string>& app_params)
 {
   ParamReplacerCtx ctx;
   ctx.app_param = getHeader(req.hdrs, PARAM_HDR, true);
c60b508e
 
aadc4574
   profiles_mut.lock();
   const SBCCallProfile* p_call_profile = getActiveProfileMatch(req, ctx);
   if(!p_call_profile) {
c60b508e
     profiles_mut.unlock();
aadc4574
     throw AmSession::Exception(500,SIP_REPLY_SERVER_INTERNAL_ERROR);
675a77ee
   }
f86ac8e0
 
aadc4574
   const SBCCallProfile& call_profile = *p_call_profile;
c5a996bc
 
aadc4574
   if(!call_profile.refuse_with.empty()) {
     if(call_profile.refuse(ctx, req) < 0) {
527d48f8
       profiles_mut.unlock();
aadc4574
       throw AmSession::Exception(500, SIP_REPLY_SERVER_INTERNAL_ERROR);
527d48f8
     }
aadc4574
     profiles_mut.unlock();
     return NULL;
f86ac8e0
   }
 
9fce821e
   SBCCallLeg* b2b_dlg = callLegCreator->create(call_profile);
f86ac8e0
 
c5a996bc
   msg_logger* logger = b2b_dlg->getCallProfile().get_logger(req);
   if (logger && call_profile.log_sip) req.log(logger);
 
f6c18400
   if (call_profile.auth_aleg_enabled) {
     // adding auth handler
     AmSessionEventHandlerFactory* uac_auth_f =
       AmPlugIn::instance()->getFactory4Seh("uac_auth");
     if (NULL == uac_auth_f)  {
       INFO("uac_auth module not loaded. uac auth for caller session NOT enabled.\n");
     } else {
       AmSessionEventHandler* h = uac_auth_f->getHandler(b2b_dlg);
 
       // we cannot use the generic AmSessionEventHandler hooks,
       // because the hooks don't work in AmB2BSession
       b2b_dlg->setAuthHandler(h);
       DBG("uac auth enabled for caller session.\n");
     }
   }
aadc4574
   profiles_mut.unlock();
f6c18400
 
aadc4574
   return b2b_dlg;
 }
da5cc058
 
a5e51cbf
 /** out-of-dialog request handling terminated */
39d24ba2
 void oodHandlingTerminated(const AmSipRequest &req, vector<AmDynInvoke*>& cc_modules, SBCCallProfile& call_profile)
a5e51cbf
 {
   for (vector<AmDynInvoke*>::iterator m = cc_modules.begin(); m != cc_modules.end(); ++m) {
     AmArg args,ret;
     args.push((AmObject*)&call_profile);
     args.push((AmObject*)&req);
     try {
       (*m)->invoke("ood_handling_terminated", args, ret);
     } catch (...) { /* ignore */ }
   }
 }
 
aadc4574
 void SBCFactory::onOoDRequest(const AmSipRequest& req)
 {
   DBG("processing message %s %s\n", req.method.c_str(), req.r_uri.c_str());  
cf2e42c1
 
   if (core_options_handling && req.method == SIP_METH_OPTIONS) {
     DBG("processing OPTIONS in core\n");
     AmSessionFactory::onOoDRequest(req);
     return;
   }
 
aadc4574
   profiles_mut.lock();
 
   ParamReplacerCtx ctx;
   ctx.app_param = getHeader(req.hdrs, PARAM_HDR, true);
 
   string profile_rule;
   const SBCCallProfile* p_call_profile = getActiveProfileMatch(req, ctx);
   if(!p_call_profile) {
     profiles_mut.unlock();
     throw AmSession::Exception(500,SIP_REPLY_SERVER_INTERNAL_ERROR);
   }
   
   SBCCallProfile call_profile(*p_call_profile);
   profiles_mut.unlock();
 
c5a996bc
   msg_logger* logger = call_profile.get_logger(req);
   if (logger && call_profile.log_sip) req.log(logger);
 
0dae3311
   ctx.call_profile = &call_profile;
aadc4574
   call_profile.eval_cc_list(ctx,req);
 
   vector<AmDynInvoke*> cc_modules;
   if(!getCCInterfaces(call_profile.cc_interfaces,cc_modules)) {
     ERROR("could not get CC interfaces\n");
     return;
   }
 
d58fb144
   // fix up variables
   call_profile.replace_cc_values(ctx,req,NULL);
c5a996bc
   if(!SBCFactory::CCRoute(req,cc_modules,call_profile)) {
a5e51cbf
     oodHandlingTerminated(req, cc_modules, call_profile);
872aabf2
     //ERROR("routing failed\n");
aadc4574
     return;
   }
 
   if (!call_profile.refuse_with.empty()) {
a5e51cbf
     oodHandlingTerminated(req, cc_modules, call_profile);
aadc4574
     if(call_profile.refuse(ctx, req) < 0) {
c60b508e
       throw AmSession::Exception(500, SIP_REPLY_SERVER_INTERNAL_ERROR);
f86ac8e0
     }
aadc4574
     return;
   }
43274be9
 
   if(req.max_forwards == 0) {
     AmSipDialog::reply_error(req, 483, SIP_REPLY_TOO_MANY_HOPS);
     return;
   }
aadc4574
   
   call_profile.fix_append_hdrs(ctx, req);
f86ac8e0
 
9f8ba5c7
   SimpleRelayCreator::Relay relay(NULL,NULL);
aadc4574
   if(req.method == SIP_METH_REGISTER) {
e59cd844
     relay = simpleRelayCreator->createRegisterRelay(call_profile, cc_modules);
f86ac8e0
   }
aadc4574
   else if((req.method == SIP_METH_SUBSCRIBE) ||
 	  (req.method == SIP_METH_REFER)){
f86ac8e0
 
e59cd844
     relay = simpleRelayCreator->createSubscriptionRelay(call_profile, cc_modules);
aadc4574
   }
   else {
e59cd844
     relay = simpleRelayCreator->createGenericRelay(call_profile, cc_modules);
aadc4574
   }
9f8ba5c7
   if (call_profile.log_sip) {
c5a996bc
     relay.first->setMsgLogger(call_profile.get_logger(req));
     relay.second->setMsgLogger(call_profile.get_logger(req));
9f8ba5c7
   }
aadc4574
 
9f8ba5c7
   if(SBCSimpleRelay::start(relay,req,call_profile)) {
     AmSipDialog::reply_error(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR, 
c5a996bc
 			     "", call_profile.log_sip ? call_profile.get_logger(req): NULL);
9f8ba5c7
     delete relay.first;
     delete relay.second;
aadc4574
   }
f86ac8e0
 }
 
527d48f8
 void SBCFactory::invoke(const string& method, const AmArg& args, 
 				AmArg& ret)
 {
   if (method == "listProfiles"){
     listProfiles(args, ret);
   } else if (method == "reloadProfiles"){
     reloadProfiles(args,ret);
   } else if (method == "loadProfile"){
     args.assertArrayFmt("u");
     loadProfile(args,ret);
   } else if (method == "reloadProfile"){
     args.assertArrayFmt("u");
     reloadProfile(args,ret);
   } else if (method == "getActiveProfile"){
     getActiveProfile(args,ret);
   } else if (method == "setActiveProfile"){
     args.assertArrayFmt("u");
     setActiveProfile(args,ret);
c60b508e
   } else if (method == "getRegexMapNames"){
     getRegexMapNames(args,ret);
   } else if (method == "setRegexMap"){
     args.assertArrayFmt("u");
     setRegexMap(args,ret);
e708b948
   } else if (method == "loadCallcontrolModules"){
     args.assertArrayFmt("s");
     loadCallcontrolModules(args,ret);
3af5aa50
   } else if (method == "postControlCmd"){
     args.assertArrayFmt("ss"); // at least call-ltag, cmd
     postControlCmd(args,ret);
527d48f8
   } else if(method == "_list"){ 
     ret.push(AmArg("listProfiles"));
     ret.push(AmArg("reloadProfiles"));
     ret.push(AmArg("reloadProfile"));
     ret.push(AmArg("loadProfile"));
     ret.push(AmArg("getActiveProfile"));
     ret.push(AmArg("setActiveProfile"));
c60b508e
     ret.push(AmArg("getRegexMapNames"));
     ret.push(AmArg("setRegexMap"));
e708b948
     ret.push(AmArg("loadCallcontrolModules"));
3af5aa50
     ret.push(AmArg("postControlCmd"));
c794c457
     ret.push(AmArg("printCallStats"));
8237ba00
   } else if(method == "printCallStats"){ 
     B2BMediaStatistics::instance()->getReport(args, ret);
527d48f8
   }  else
     throw AmDynInvoke::NotImplemented(method);
 }
 
 void SBCFactory::listProfiles(const AmArg& args, AmArg& ret) {
   profiles_mut.lock();
   for (std::map<string, SBCCallProfile>::iterator it=
 	 call_profiles.begin(); it != call_profiles.end(); it++) {
     AmArg p;
     p["name"] = it->first;
     p["md5"] = it->second.md5hash;
     p["path"] = it->second.profile_file;
     ret.push((p));
   }
   profiles_mut.unlock();
 }
 
 void SBCFactory::reloadProfiles(const AmArg& args, AmArg& ret) {
   std::map<string, SBCCallProfile> new_call_profiles;
   
   bool failed = false;
   string res = "OK";
   AmArg profile_list;
   profiles_mut.lock();
   for (std::map<string, SBCCallProfile>::iterator it=
 	 call_profiles.begin(); it != call_profiles.end(); it++) {
     new_call_profiles[it->first] = SBCCallProfile();
     if (!new_call_profiles[it->first].readFromConfiguration(it->first,
 							    it->second.profile_file)) {
       ERROR("reading call profile file '%s'\n", it->second.profile_file.c_str());
       res = "Error reading call profile for "+it->first+" from "+it->second.profile_file+
 	+"; no profiles reloaded";
       failed = true;
       break;
     }
     AmArg p;
     p["name"] = it->first;
     p["md5"] = it->second.md5hash;
     p["path"] = it->second.profile_file;
     profile_list.push(p);
   }
   if (!failed) {
     call_profiles = new_call_profiles;
     ret.push(200);
   } else {
     ret.push(500);
   }
   ret.push(res);
   ret.push(profile_list);
   profiles_mut.unlock();
 }
 
 void SBCFactory::reloadProfile(const AmArg& args, AmArg& ret) {
   bool failed = false;
   string res = "OK";
   AmArg p;
   if (!args[0].hasMember("name")) {
     ret.push(400);
     ret.push("Parameters error: expected ['name': profile_name] ");
     return;
   }
 
   profiles_mut.lock();
   std::map<string, SBCCallProfile>::iterator it=
     call_profiles.find(args[0]["name"].asCStr());
   if (it == call_profiles.end()) {
     res = "profile '"+string(args[0]["name"].asCStr())+"' not found";
     failed = true;
   } else {
     SBCCallProfile new_cp;
     if (!new_cp.readFromConfiguration(it->first, it->second.profile_file)) {
       ERROR("reading call profile file '%s'\n", it->second.profile_file.c_str());
       res = "Error reading call profile for "+it->first+" from "+it->second.profile_file;
       failed = true;
     } else {
       it->second = new_cp;
       p["name"] = it->first;
       p["md5"] = it->second.md5hash;
       p["path"] = it->second.profile_file;
     }
   }
   profiles_mut.unlock();
 
   if (!failed) {
     ret.push(200);
     ret.push(res);
     ret.push(p);
   } else {
     ret.push(500);
     ret.push(res);
   }
 }
 
 void SBCFactory::loadProfile(const AmArg& args, AmArg& ret) {
   if (!args[0].hasMember("name") || !args[0].hasMember("path")) {
     ret.push(400);
     ret.push("Parameters error: expected ['name': profile_name] "
 	     "and ['path': profile_path]");
     return;
   }
   SBCCallProfile cp;
   if (!cp.readFromConfiguration(args[0]["name"].asCStr(), args[0]["path"].asCStr())) {
     ret.push(500);
     ret.push("Error reading sbc call profile for "+string(args[0]["name"].asCStr())+
 	     " from file "+string(args[0]["path"].asCStr()));
     return;
   }
 
   profiles_mut.lock();
   call_profiles[args[0]["name"].asCStr()] = cp;
   profiles_mut.unlock();
   ret.push(200);
   ret.push("OK");
   AmArg p;
   p["name"] = args[0]["name"];
   p["md5"] = cp.md5hash;
   p["path"] = args[0]["path"];
   ret.push(p);
 }
 
 void SBCFactory::getActiveProfile(const AmArg& args, AmArg& ret) {
   profiles_mut.lock();
   AmArg p;
c60b508e
   for (vector<string>::iterator it=active_profile.begin();
        it != active_profile.end(); it++) {
     p["active_profile"].push(*it);
   }
527d48f8
   profiles_mut.unlock();
   ret.push(200);
   ret.push("OK");
   ret.push(p);
 }
 
 void SBCFactory::setActiveProfile(const AmArg& args, AmArg& ret) {
   if (!args[0].hasMember("active_profile")) {
     ret.push(400);
c60b508e
     ret.push("Parameters error: expected ['active_profile': <active_profile list>] ");
527d48f8
     return;
   }
   profiles_mut.lock();
c60b508e
   active_profile = explode(args[0]["active_profile"].asCStr(), ",");
527d48f8
   profiles_mut.unlock();
   ret.push(200);
   ret.push("OK");
   AmArg p;
   p["active_profile"] = args[0]["active_profile"];
   ret.push(p);  
 }
 
c60b508e
 void SBCFactory::getRegexMapNames(const AmArg& args, AmArg& ret) {
   AmArg p;
   vector<string> reg_names = regex_mappings.getNames();
   for (vector<string>::iterator it=reg_names.begin();
        it != reg_names.end(); it++) {
     p["regex_maps"].push(*it);
   }
   ret.push(200);
   ret.push("OK");
   ret.push(p);
 }
 
 void SBCFactory::setRegexMap(const AmArg& args, AmArg& ret) {
   if (!args[0].hasMember("name") || !args[0].hasMember("file") ||
       !isArgCStr(args[0]["name"]) || !isArgCStr(args[0]["file"])) {
     ret.push(400);
     ret.push("Parameters error: expected ['name': <name>, 'file': <file name>]");
     return;
   }
 
   string m_name = args[0]["name"].asCStr();
   string m_file = args[0]["file"].asCStr();
   RegexMappingVector v;
   if (!read_regex_mapping(m_file, "=>", "SBC regex mapping", v)) {
     ERROR("reading regex mapping from '%s'\n", m_file.c_str());
     ret.push(401);
     ret.push("Error reading regex mapping from file");
     return;
   }
   regex_mappings.setRegexMap(m_name, v);
   ret.push(200);
   ret.push("OK");
 }
f86ac8e0
 
e708b948
 void SBCFactory::loadCallcontrolModules(const AmArg& args, AmArg& ret) {
   string load_cc_plugins = args[0].asCStr();
   if (!load_cc_plugins.empty()) {
     INFO("loading call control plugins '%s' from '%s'\n",
 	 load_cc_plugins.c_str(), AmConfig::PlugInPath.c_str());
     if (AmPlugIn::instance()->load(AmConfig::PlugInPath, load_cc_plugins) < 0) {
       ERROR("loading call control plugins '%s' from '%s'\n",
 	    load_cc_plugins.c_str(), AmConfig::PlugInPath.c_str());
       
       ret.push(500);
       ret.push("Failed - please see server logs\n");
       return;
     }
   }
   ret.push(200);
   ret.push("OK");
 }
 
3af5aa50
 void SBCFactory::postControlCmd(const AmArg& args, AmArg& ret) {
   SBCControlEvent* evt;
   if (args.size()<3) {
     evt = new SBCControlEvent(args[1].asCStr());
   } else {
     evt = new SBCControlEvent(args[1].asCStr(), args[2]);
   }
   if (!AmSessionContainer::instance()->postEvent(args[0].asCStr(), evt)) {
     ret.push(404);
     ret.push("Not found");
   } else {
     ret.push(202);
     ret.push("Accepted");
   }
 }
 
aadc4574
 bool SBCFactory::CCRoute(const AmSipRequest& req,
 			 vector<AmDynInvoke*>& cc_modules,
c5a996bc
 			 SBCCallProfile& call_profile)
aadc4574
 {
   vector<AmDynInvoke*>::iterator cc_mod=cc_modules.begin();
 
   for (CCInterfaceListIteratorT cc_it=call_profile.cc_interfaces.begin();
        cc_it != call_profile.cc_interfaces.end(); cc_it++) {
     CCInterface& cc_if = *cc_it;
 
     AmArg di_args,ret;
     di_args.push(cc_if.cc_name);
     di_args.push(""); //getLocalTag()
     di_args.push((AmObject*)&call_profile);
acfec10d
     di_args.push((AmObject*)&req);
aadc4574
     di_args.push(AmArg());
     di_args.back().push((int) 0);
     di_args.back().push((int) 0);
     di_args.push(AmArg());
     AmArg& vals = di_args.back();
     vals.assertStruct();
     for (map<string, string>::iterator it = cc_if.cc_values.begin();
 	 it != cc_if.cc_values.end(); it++) {
       vals[it->first] = it->second;
     }
 
     di_args.push(0); // current timer ID
 
     try {
       (*cc_mod)->invoke("route", di_args, ret);
     } catch (const AmArg::OutOfBoundsException& e) {
       ERROR("OutOfBoundsException executing call control interface route "
 	    "module '%s' named '%s', parameters '%s'\n",
 	    cc_if.cc_module.c_str(), cc_if.cc_name.c_str(),
 	    AmArg::print(di_args).c_str());
       AmBasicSipDialog::reply_error(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR);
       return false;
     } catch (const AmArg::TypeMismatchException& e) {
       ERROR("TypeMismatchException executing call control interface route "
 	    "module '%s' named '%s', parameters '%s'\n",
 	    cc_if.cc_module.c_str(), cc_if.cc_name.c_str(),
 	    AmArg::print(di_args).c_str());
       AmBasicSipDialog::reply_error(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR);
       return false;
4d0cd69e
     } catch (...) {
       ERROR("Exception occured when executing call control interface route "
 	    "module '%s' named '%s', parameters '%s'\n",
 	    cc_if.cc_module.c_str(), cc_if.cc_name.c_str(),
 	    AmArg::print(di_args).c_str());
       AmBasicSipDialog::reply_error(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR);
       return false;
aadc4574
     }
 
     // evaluate ret
     if (isArgArray(ret)) {
       for (size_t i=0;i<ret.size();i++) {
 	if (!isArgArray(ret[i]) || !ret[i].size())
 	  continue;
 	if (!isArgInt(ret[i][SBC_CC_ACTION])) {
 	  ERROR("in call control module '%s' - action type not int\n",
 		cc_if.cc_name.c_str());
 	  continue;
 	}
 	switch (ret[i][SBC_CC_ACTION].asInt()) {
 	case SBC_CC_DROP_ACTION: {
bcfbc23b
 	  DBG("dropping request/call on call control action DROP from '%s'\n",
aadc4574
 	      cc_if.cc_name.c_str());
 	  return false;
 	}
 
 	case SBC_CC_REFUSE_ACTION: {
 	  if (ret[i].size() < 3 ||
 	      !isArgInt(ret[i][SBC_CC_REFUSE_CODE]) ||
 	      !isArgCStr(ret[i][SBC_CC_REFUSE_REASON])) {
 	    ERROR("in call control module '%s' - REFUSE"
 		  " action parameters missing/wrong: '%s'\n",
 		  cc_if.cc_name.c_str(), AmArg::print(ret[i]).c_str());
 	    continue;
 	  }
 	  string headers;
 	  if (ret[i].size() > SBC_CC_REFUSE_HEADERS) {
 	    for (size_t h=0;h<ret[i][SBC_CC_REFUSE_HEADERS].size();h++)
 	      headers += string(ret[i][SBC_CC_REFUSE_HEADERS][h].asCStr()) + CRLF;
 	  }
 
 	  DBG("replying with %d %s on call control action "
 	      "REFUSE from '%s' headers='%s'\n",
 	      ret[i][SBC_CC_REFUSE_CODE].asInt(), 
 	      ret[i][SBC_CC_REFUSE_REASON].asCStr(),
 	      cc_if.cc_name.c_str(), headers.c_str());
 
 	  AmBasicSipDialog::reply_error(req,
 	        ret[i][SBC_CC_REFUSE_CODE].asInt(), 
 		ret[i][SBC_CC_REFUSE_REASON].asCStr(),
c5a996bc
 		headers, call_profile.log_sip ? call_profile.get_logger(req): NULL);
aadc4574
 
 	  return false;
 	}
 
511eeca9
 	case SBC_CC_SET_CALL_TIMER_ACTION:
           // just ignore
           continue;
 
aadc4574
 	default: {
 	  ERROR("unknown call control action: '%s'\n", 
 		AmArg::print(ret[i]).c_str());
 	  continue;
 	}
 
 	}
       }
     }
 
     cc_mod++;
   }
 
   return true;
 }