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.
232 lines
6.8 KiB
232 lines
6.8 KiB
/*
|
|
* Copyright (C) 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
|
|
*/
|
|
|
|
#include "AmPlugIn.h"
|
|
#include "log.h"
|
|
#include "AmArg.h"
|
|
#include "AmSipHeaders.h"
|
|
|
|
#include "CCParallelCalls.h"
|
|
#include "SBCCallControlAPI.h"
|
|
|
|
#include <string.h>
|
|
|
|
#define SBCVAR_PARALLEL_CALLS_UUID "uuid"
|
|
|
|
unsigned int CCParallelCalls::refuse_code = 402;
|
|
string CCParallelCalls::refuse_reason = "Too Many Simultaneous Calls";
|
|
|
|
CCParallelCalls::CCParallelCalls()
|
|
{
|
|
}
|
|
|
|
CCParallelCalls::~CCParallelCalls() { }
|
|
|
|
int CCParallelCalls::onLoad() {
|
|
AmConfigReader cfg;
|
|
|
|
if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) {
|
|
INFO(MOD_NAME "configuration file (%s) not found, "
|
|
"assuming default configuration is fine\n",
|
|
(AmConfig::ModConfigPath + string(MOD_NAME ".conf")).c_str());
|
|
return 0;
|
|
}
|
|
|
|
refuse_reason = cfg.hasParameter("refuse_reason") ?
|
|
cfg.getParameter("refuse_reason") : refuse_reason;
|
|
|
|
if (cfg.hasParameter("refuse_code")) {
|
|
if (str2int(cfg.getParameter("refuse_code"), refuse_code)) {
|
|
ERROR("refuse_code '%s' not understood\n", cfg.getParameter("refuse_code").c_str());
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CCParallelCalls::invoke(const string& method, const AmArg& args, AmArg& ret)
|
|
{
|
|
// DBG("CCParallelCalls: %s(%s)\n", method.c_str(), AmArg::print(args).c_str());
|
|
|
|
if(method == "start"){
|
|
SBCCallProfile* call_profile =
|
|
dynamic_cast<SBCCallProfile*>(args[CC_API_PARAMS_CALL_PROFILE].asObject());
|
|
|
|
start(args[CC_API_PARAMS_CC_NAMESPACE].asCStr(),
|
|
args[CC_API_PARAMS_LTAG].asCStr(), call_profile,
|
|
args[CC_API_PARAMS_CFGVALUES], ret);
|
|
|
|
} else if(method == "connect"){
|
|
// no action
|
|
|
|
} else if(method == "end"){
|
|
args[CC_API_PARAMS_TIMESTAMPS].assertArrayFmt("iiiiii");
|
|
SBCCallProfile* call_profile =
|
|
dynamic_cast<SBCCallProfile*>(args[CC_API_PARAMS_CALL_PROFILE].asObject());
|
|
|
|
end(args[CC_API_PARAMS_CC_NAMESPACE].asCStr(),
|
|
args[CC_API_PARAMS_LTAG].asCStr(), call_profile);
|
|
|
|
} else if(method == CC_INTERFACE_MAND_VALUES_METHOD){
|
|
ret.push("uuid");
|
|
} else if(method == "_list"){
|
|
ret.push("start");
|
|
ret.push("connect");
|
|
ret.push("end");
|
|
}
|
|
else
|
|
throw AmDynInvoke::NotImplemented(method);
|
|
}
|
|
|
|
void CCParallelCalls::start(const string& cc_namespace,
|
|
const string& ltag, SBCCallProfile* call_profile,
|
|
const AmArg& values, AmArg& res) {
|
|
#define REFUSE_WITH_SERVER_INTERNAL_ERROR \
|
|
res.push(AmArg()); \
|
|
AmArg& res_cmd = res[0]; \
|
|
res_cmd[SBC_CC_ACTION] = SBC_CC_REFUSE_ACTION; \
|
|
res_cmd[SBC_CC_REFUSE_CODE] = 500; \
|
|
res_cmd[SBC_CC_REFUSE_REASON] = SIP_REPLY_SERVER_INTERNAL_ERROR; \
|
|
return;
|
|
|
|
if (!call_profile) {
|
|
ERROR("internal: call_profile object not found in parameters\n");
|
|
REFUSE_WITH_SERVER_INTERNAL_ERROR;
|
|
return;
|
|
}
|
|
|
|
if (!values.hasMember("uuid") || !isArgCStr(values["uuid"]) ||
|
|
!strlen(values["uuid"].asCStr())) {
|
|
ERROR("configuration error: uuid missing for parallel calls call control!\n");
|
|
REFUSE_WITH_SERVER_INTERNAL_ERROR;
|
|
}
|
|
|
|
string uuid = values["uuid"].asCStr();
|
|
|
|
call_profile->cc_vars[cc_namespace+"::"+SBCVAR_PARALLEL_CALLS_UUID] = uuid;
|
|
|
|
unsigned int max_calls = 1; // default
|
|
if (values.hasMember("max_calls") && isArgCStr(values["max_calls"])) {
|
|
if (str2int(values["max_calls"].asCStr(), max_calls)) {
|
|
ERROR("max_calls '%s' could not be interpreted!\n", values["max_calls"].asCStr());
|
|
REFUSE_WITH_SERVER_INTERNAL_ERROR;
|
|
}
|
|
}
|
|
|
|
DBG("enforcing limit of %i calls for uuid '%s'\n", max_calls, uuid.c_str());
|
|
|
|
bool do_limit = !max_calls;
|
|
unsigned int current_calls = 0;
|
|
if (max_calls) {
|
|
call_control_calls_mut.lock();
|
|
map<string, unsigned int>::iterator it=call_control_calls.find(uuid);
|
|
if (it==call_control_calls.end()) {
|
|
call_control_calls[uuid] = current_calls = 1;
|
|
} else {
|
|
if (it->second < max_calls) {
|
|
it->second++;
|
|
} else {
|
|
do_limit = true;
|
|
}
|
|
current_calls = it->second;
|
|
}
|
|
}
|
|
|
|
call_control_calls_mut.unlock();
|
|
|
|
DBG("uuid %s has %u active calls (limit = %s)\n",
|
|
uuid.c_str(), current_calls, do_limit?"true":"false");
|
|
|
|
if (do_limit) {
|
|
res.push(AmArg());
|
|
AmArg& res_cmd = res[0];
|
|
res_cmd[SBC_CC_ACTION] = SBC_CC_REFUSE_ACTION;
|
|
res_cmd[SBC_CC_REFUSE_CODE] = (int)refuse_code;
|
|
res_cmd[SBC_CC_REFUSE_REASON] = refuse_reason;
|
|
}
|
|
|
|
#undef REFUSE_WITH_SERVER_INTERNAL_ERROR
|
|
}
|
|
|
|
void CCParallelCalls::end(const string& cc_namespace, const string& ltag,
|
|
SBCCallProfile* call_profile) {
|
|
if (!call_profile) {
|
|
ERROR("internal: call_profile object not found in parameters\n");
|
|
return;
|
|
}
|
|
|
|
SBCVarMapIteratorT vars_it =
|
|
call_profile->cc_vars.find(cc_namespace+"::"+SBCVAR_PARALLEL_CALLS_UUID);
|
|
if (vars_it == call_profile->cc_vars.end() || !isArgCStr(vars_it->second)) {
|
|
ERROR("internal: could not find UUID for ending call '%s'\n", ltag.c_str());
|
|
return;
|
|
}
|
|
string uuid = vars_it->second.asCStr();
|
|
call_profile->cc_vars.erase(cc_namespace+"::"+SBCVAR_PARALLEL_CALLS_UUID);
|
|
|
|
unsigned int new_call_count = 0;
|
|
|
|
call_control_calls_mut.lock();
|
|
if (call_control_calls[uuid] > 1) {
|
|
new_call_count = --call_control_calls[uuid];
|
|
} else {
|
|
call_control_calls.erase(uuid);
|
|
}
|
|
call_control_calls_mut.unlock();
|
|
|
|
DBG("uuid '%s' now has %u active calls\n", uuid.c_str(), new_call_count);
|
|
}
|
|
|
|
CCParallelCalls* CCParallelCalls::_instance=0;
|
|
|
|
CCParallelCalls* CCParallelCalls::instance()
|
|
{
|
|
if(!_instance)
|
|
_instance = new CCParallelCalls();
|
|
return _instance;
|
|
}
|
|
|
|
class CCParallelCallsFactory : public AmDynInvokeFactory
|
|
{
|
|
public:
|
|
CCParallelCallsFactory(const string& name)
|
|
: AmDynInvokeFactory(name) { }
|
|
|
|
AmDynInvoke* getInstance(){
|
|
return CCParallelCalls::instance();
|
|
}
|
|
|
|
int onLoad(){
|
|
if (CCParallelCalls::instance()->onLoad())
|
|
return -1;
|
|
|
|
DBG("parallel call control loaded.\n");
|
|
|
|
return 0;
|
|
}
|
|
};
|
|
EXPORT_PLUGIN_CLASS_FACTORY(CCParallelCallsFactory,MOD_NAME);
|