mirror of https://github.com/sipwise/sems.git
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.
820 lines
24 KiB
820 lines
24 KiB
/*
|
|
* Copyright (C) 2010-2011 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.
|
|
*
|
|
* 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"
|
|
|
|
#include "SBCCallControlAPI.h"
|
|
|
|
#include "log.h"
|
|
#include "AmUtils.h"
|
|
#include "AmAudio.h"
|
|
#include "AmPlugIn.h"
|
|
#include "AmMediaProcessor.h"
|
|
#include "AmConfigReader.h"
|
|
#include "AmSessionContainer.h"
|
|
#include "AmSipHeaders.h"
|
|
#include "SBCSimpleRelay.h"
|
|
#include "RegisterDialog.h"
|
|
#include "SubscriptionDialog.h"
|
|
#include "sip/pcap_logger.h"
|
|
#include "sip/sip_parser.h"
|
|
#include "sip/sip_trans.h"
|
|
|
|
#include "HeaderFilter.h"
|
|
#include "ParamReplacer.h"
|
|
#include "SDPFilter.h"
|
|
#include "SBCCallLeg.h"
|
|
|
|
#include "AmEventQueueProcessor.h"
|
|
|
|
#include "SubscriptionDialog.h"
|
|
#include "RegisterDialog.h"
|
|
#include "RegisterCache.h"
|
|
|
|
#include <algorithm>
|
|
|
|
using std::map;
|
|
|
|
#define MOD_NAME "sbc"
|
|
|
|
|
|
EXPORT_MODULE_FACTORY(SBCFactory);
|
|
DEFINE_MODULE_INSTANCE(SBCFactory, MOD_NAME);
|
|
|
|
// helper functions
|
|
|
|
bool getCCInterfaces(CCInterfaceListT& cc_interfaces, vector<AmDynInvoke*>& cc_modules)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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";
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SBCCallLeg* CallLegCreator::create(const SBCCallProfile& call_profile)
|
|
{
|
|
return new SBCCallLeg(call_profile, new AmSipDialog());
|
|
}
|
|
|
|
SBCCallLeg* CallLegCreator::create(SBCCallLeg* caller)
|
|
{
|
|
return new SBCCallLeg(caller);
|
|
}
|
|
|
|
SimpleRelayCreator::Relay
|
|
SimpleRelayCreator::createRegisterRelay(SBCCallProfile& call_profile,
|
|
vector<AmDynInvoke*> &cc_modules)
|
|
{
|
|
return SimpleRelayCreator::Relay(new RegisterDialog(call_profile, cc_modules),
|
|
new RegisterDialog(call_profile, cc_modules));
|
|
}
|
|
|
|
SimpleRelayCreator::Relay
|
|
SimpleRelayCreator::createSubscriptionRelay(SBCCallProfile& call_profile,
|
|
vector<AmDynInvoke*> &cc_modules)
|
|
{
|
|
return SimpleRelayCreator::Relay(new SubscriptionDialog(call_profile, cc_modules),
|
|
new SubscriptionDialog(call_profile, cc_modules));
|
|
}
|
|
|
|
SimpleRelayCreator::Relay
|
|
SimpleRelayCreator::createGenericRelay(SBCCallProfile& call_profile,
|
|
vector<AmDynInvoke*> &cc_modules)
|
|
{
|
|
return SimpleRelayCreator::Relay(new SimpleRelayDialog(call_profile, cc_modules),
|
|
new SimpleRelayDialog(call_profile, cc_modules));
|
|
}
|
|
|
|
SBCFactory::SBCFactory(const string& _app_name)
|
|
: AmSessionFactory(_app_name),
|
|
AmDynInvokeFactory(_app_name),
|
|
core_options_handling(false),
|
|
callLegCreator(new CallLegCreator()),
|
|
simpleRelayCreator(new SimpleRelayCreator())
|
|
{
|
|
}
|
|
|
|
SBCFactory::~SBCFactory() {
|
|
RegisterCache::dispose();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
session_timer_fact = AmPlugIn::instance()->getFactory4Seh("session_timer");
|
|
if(!session_timer_fact) {
|
|
WARN("session_timer plug-in not loaded - "
|
|
"SIP Session Timers will not be supported\n");
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
active_profile = explode(cfg.getParameter("active_profile"), ",");
|
|
if (active_profile.empty()) {
|
|
ERROR("active_profile not set.\n");
|
|
return -1;
|
|
}
|
|
|
|
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;
|
|
}
|
|
active_profile_s+=*it;
|
|
if (it != active_profile.end()-1)
|
|
active_profile_s+=", ";
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
core_options_handling = cfg.getParameter("core_options_handling") == "yes";
|
|
DBG("OPTIONS messages handled by the core: %s\n", core_options_handling?"yes":"no");
|
|
|
|
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;
|
|
}
|
|
|
|
subnot_processor.addThreads(cfg.getParameterInt("out_of_dialog_threads",
|
|
DEFAULT_OOD_THREADS));
|
|
RegisterCache::instance()->start();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** get the first matching profile name from active profiles */
|
|
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++) {
|
|
|
|
if (it->empty())
|
|
continue;
|
|
|
|
if (*it == "$(paramhdr)")
|
|
profile = get_header_keyvalue(ctx.app_param,"profile");
|
|
else if (*it == "$(ruri.user)")
|
|
profile = req.user;
|
|
else
|
|
profile = ctx.replaceParameters(*it, "active_profile", req);
|
|
|
|
if (!profile.empty()) {
|
|
profile_rule = *it;
|
|
break;
|
|
}
|
|
}
|
|
|
|
DBG("active profile = %s\n", profile.c_str());
|
|
|
|
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",
|
|
profile.c_str(), profile_rule.c_str());
|
|
|
|
return NULL;
|
|
}
|
|
|
|
DBG("using call profile '%s' (from matching active_profile rule '%s')\n",
|
|
profile.c_str(), profile_rule.c_str());
|
|
|
|
return &prof_it->second;
|
|
}
|
|
|
|
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);
|
|
|
|
profiles_mut.lock();
|
|
const SBCCallProfile* p_call_profile = getActiveProfileMatch(req, ctx);
|
|
if(!p_call_profile) {
|
|
profiles_mut.unlock();
|
|
throw AmSession::Exception(500,SIP_REPLY_SERVER_INTERNAL_ERROR);
|
|
}
|
|
|
|
const SBCCallProfile& call_profile = *p_call_profile;
|
|
|
|
if(!call_profile.refuse_with.empty()) {
|
|
if(call_profile.refuse(ctx, req) < 0) {
|
|
profiles_mut.unlock();
|
|
throw AmSession::Exception(500, SIP_REPLY_SERVER_INTERNAL_ERROR);
|
|
}
|
|
profiles_mut.unlock();
|
|
return NULL;
|
|
}
|
|
|
|
SBCCallLeg* b2b_dlg = callLegCreator->create(call_profile);
|
|
|
|
msg_logger* logger = b2b_dlg->getCallProfile().get_logger(req);
|
|
if (logger && call_profile.log_sip) req.log(logger);
|
|
|
|
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");
|
|
}
|
|
}
|
|
profiles_mut.unlock();
|
|
|
|
return b2b_dlg;
|
|
}
|
|
|
|
/** out-of-dialog request handling terminated */
|
|
void oodHandlingTerminated(const AmSipRequest &req, vector<AmDynInvoke*>& cc_modules, SBCCallProfile& call_profile)
|
|
{
|
|
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 */ }
|
|
}
|
|
}
|
|
|
|
void SBCFactory::onOoDRequest(const AmSipRequest& req)
|
|
{
|
|
DBG("processing message %s %s\n", req.method.c_str(), req.r_uri.c_str());
|
|
|
|
if (core_options_handling && req.method == SIP_METH_OPTIONS) {
|
|
DBG("processing OPTIONS in core\n");
|
|
AmSessionFactory::onOoDRequest(req);
|
|
return;
|
|
}
|
|
|
|
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();
|
|
|
|
msg_logger* logger = call_profile.get_logger(req);
|
|
if (logger && call_profile.log_sip) req.log(logger);
|
|
|
|
ctx.call_profile = &call_profile;
|
|
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;
|
|
}
|
|
|
|
// fix up variables
|
|
call_profile.replace_cc_values(ctx,req,NULL);
|
|
if(!SBCFactory::CCRoute(req,cc_modules,call_profile)) {
|
|
oodHandlingTerminated(req, cc_modules, call_profile);
|
|
//ERROR("routing failed\n");
|
|
return;
|
|
}
|
|
|
|
if (!call_profile.refuse_with.empty()) {
|
|
oodHandlingTerminated(req, cc_modules, call_profile);
|
|
if(call_profile.refuse(ctx, req) < 0) {
|
|
throw AmSession::Exception(500, SIP_REPLY_SERVER_INTERNAL_ERROR);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(req.max_forwards == 0) {
|
|
AmSipDialog::reply_error(req, 483, SIP_REPLY_TOO_MANY_HOPS);
|
|
return;
|
|
}
|
|
|
|
call_profile.fix_append_hdrs(ctx, req);
|
|
|
|
SimpleRelayCreator::Relay relay(NULL,NULL);
|
|
if(req.method == SIP_METH_REGISTER) {
|
|
relay = simpleRelayCreator->createRegisterRelay(call_profile, cc_modules);
|
|
}
|
|
else if((req.method == SIP_METH_SUBSCRIBE) ||
|
|
(req.method == SIP_METH_REFER)){
|
|
|
|
relay = simpleRelayCreator->createSubscriptionRelay(call_profile, cc_modules);
|
|
}
|
|
else {
|
|
relay = simpleRelayCreator->createGenericRelay(call_profile, cc_modules);
|
|
}
|
|
if (call_profile.log_sip) {
|
|
relay.first->setMsgLogger(call_profile.get_logger(req));
|
|
relay.second->setMsgLogger(call_profile.get_logger(req));
|
|
}
|
|
|
|
if(SBCSimpleRelay::start(relay,req,call_profile)) {
|
|
AmSipDialog::reply_error(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR,
|
|
"", call_profile.log_sip ? call_profile.get_logger(req): NULL);
|
|
delete relay.first;
|
|
delete relay.second;
|
|
}
|
|
}
|
|
|
|
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);
|
|
} else if (method == "getRegexMapNames"){
|
|
getRegexMapNames(args,ret);
|
|
} else if (method == "setRegexMap"){
|
|
args.assertArrayFmt("u");
|
|
setRegexMap(args,ret);
|
|
} else if (method == "loadCallcontrolModules"){
|
|
args.assertArrayFmt("s");
|
|
loadCallcontrolModules(args,ret);
|
|
} else if (method == "postControlCmd"){
|
|
args.assertArrayFmt("ss"); // at least call-ltag, cmd
|
|
postControlCmd(args,ret);
|
|
} 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"));
|
|
ret.push(AmArg("getRegexMapNames"));
|
|
ret.push(AmArg("setRegexMap"));
|
|
ret.push(AmArg("loadCallcontrolModules"));
|
|
ret.push(AmArg("postControlCmd"));
|
|
ret.push(AmArg("printCallStats"));
|
|
} else if(method == "printCallStats"){
|
|
B2BMediaStatistics::instance()->getReport(args, ret);
|
|
} 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;
|
|
for (vector<string>::iterator it=active_profile.begin();
|
|
it != active_profile.end(); it++) {
|
|
p["active_profile"].push(*it);
|
|
}
|
|
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);
|
|
ret.push("Parameters error: expected ['active_profile': <active_profile list>] ");
|
|
return;
|
|
}
|
|
profiles_mut.lock();
|
|
active_profile = explode(args[0]["active_profile"].asCStr(), ",");
|
|
profiles_mut.unlock();
|
|
ret.push(200);
|
|
ret.push("OK");
|
|
AmArg p;
|
|
p["active_profile"] = args[0]["active_profile"];
|
|
ret.push(p);
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
bool SBCFactory::CCRoute(const AmSipRequest& req,
|
|
vector<AmDynInvoke*>& cc_modules,
|
|
SBCCallProfile& call_profile)
|
|
{
|
|
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);
|
|
di_args.push((AmObject*)&req);
|
|
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;
|
|
} 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;
|
|
}
|
|
|
|
// 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: {
|
|
DBG("dropping request/call on call control action DROP from '%s'\n",
|
|
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(),
|
|
headers, call_profile.log_sip ? call_profile.get_logger(req): NULL);
|
|
|
|
return false;
|
|
}
|
|
|
|
case SBC_CC_SET_CALL_TIMER_ACTION:
|
|
// just ignore
|
|
continue;
|
|
|
|
default: {
|
|
ERROR("unknown call control action: '%s'\n",
|
|
AmArg::print(ret[i]).c_str());
|
|
continue;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
cc_mod++;
|
|
}
|
|
|
|
return true;
|
|
}
|