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.
301 lines
9.9 KiB
301 lines
9.9 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 "RestModule.h"
|
|
#include "RestParams.h"
|
|
|
|
#include "SBCCallControlAPI.h"
|
|
|
|
#include <string.h>
|
|
#include <algorithm>
|
|
#include <curl/curl.h>
|
|
|
|
using namespace std;
|
|
|
|
class RestModuleFactory : public AmDynInvokeFactory
|
|
{
|
|
public:
|
|
RestModuleFactory(const string& name)
|
|
: AmDynInvokeFactory(name) {}
|
|
|
|
AmDynInvoke* getInstance(){
|
|
return RestModule::instance();
|
|
}
|
|
|
|
int onLoad(){
|
|
if (RestModule::instance()->onLoad())
|
|
return -1;
|
|
|
|
DBG("template call control loaded.\n");
|
|
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
EXPORT_PLUGIN_CLASS_FACTORY(RestModuleFactory, MOD_NAME);
|
|
|
|
RestModule* RestModule::_instance=0;
|
|
|
|
RestModule* RestModule::instance()
|
|
{
|
|
if(!_instance)
|
|
_instance = new RestModule();
|
|
return _instance;
|
|
}
|
|
|
|
RestModule::RestModule()
|
|
{
|
|
}
|
|
|
|
RestModule::~RestModule() { }
|
|
|
|
int RestModule::onLoad()
|
|
{
|
|
// FIXME: integrate with other modules using libcurl, better handling of
|
|
// multithreaded access could be helpful, etc...
|
|
|
|
static bool globally_initialized = false;
|
|
|
|
if (!globally_initialized) {
|
|
CURLcode res = curl_global_init(CURL_GLOBAL_ALL);
|
|
if (res != 0) {
|
|
ERROR("can not initialize libcurl: %d\n", res);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void RestModule::invoke(const string& method, const AmArg& args, AmArg& ret)
|
|
{
|
|
DBG("RestModule: %s(%s)\n", method.c_str(), AmArg::print(args).c_str());
|
|
|
|
if(method == "start"){
|
|
// INFO("--------------------------------------------------------------\n");
|
|
// INFO("Got call control start ltag '%s' start_ts %i.%i\n",
|
|
// args.get(0).asCStr(), args[2][0].asInt(), args[2][1].asInt());
|
|
// INFO("---- dumping CC values ----\n");
|
|
// for (AmArg::ValueStruct::const_iterator it =
|
|
// args.get(CC_API_PARAMS_CFGVALUES).begin();
|
|
// it != args.get(CC_API_PARAMS_CFGVALUES).end(); it++) {
|
|
// INFO(" CDR value '%s' = '%s'\n", it->first.c_str(), it->second.asCStr());
|
|
// }
|
|
// INFO("--------------------------------------------------------------\n");
|
|
|
|
// cc_name, ltag, call profile, timestamps, [[key: val], ...], timer_id
|
|
//args.assertArrayFmt("ssoaui");
|
|
//args[CC_API_PARAMS_TIMESTAMPS].assertArrayFmt("iiiiii");
|
|
|
|
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_TIMESTAMPS][CC_API_TS_START_SEC].asInt(),
|
|
args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_START_USEC].asInt(),
|
|
args[CC_API_PARAMS_CFGVALUES],
|
|
args[CC_API_PARAMS_TIMERID].asInt(), ret);
|
|
|
|
} else if(method == "connect"){
|
|
// INFO("--------------------------------------------------------------\n");
|
|
// INFO("Got CDR connect ltag '%s' other_ltag '%s', connect_ts %i.%i\n",
|
|
// args[CC_API_PARAMS_LTAG].asCStr(),
|
|
// args[CC_API_PARAMS_OTHERID].asCStr(),
|
|
// args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_CONNECT_SEC].asInt(),
|
|
// args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_CONNECT_USEC].asInt());
|
|
// INFO("--------------------------------------------------------------\n");
|
|
// cc_name, ltag, call_profile, other_ltag, connect_ts_sec, connect_ts_usec
|
|
// args.assertArrayFmt("ssoas");
|
|
// args[CC_API_PARAMS_TIMESTAMPS].assertArrayFmt("iiiiii");
|
|
|
|
SBCCallProfile* call_profile =
|
|
dynamic_cast<SBCCallProfile*>(args[CC_API_PARAMS_CALL_PROFILE].asObject());
|
|
|
|
connect(args[CC_API_PARAMS_CC_NAMESPACE].asCStr(),
|
|
args[CC_API_PARAMS_LTAG].asCStr(),
|
|
call_profile,
|
|
args[CC_API_PARAMS_OTHERID].asCStr(),
|
|
args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_CONNECT_SEC].asInt(),
|
|
args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_CONNECT_USEC].asInt());
|
|
|
|
} else if(method == "end"){
|
|
// INFO("--------------------------------------------------------------\n");
|
|
// INFO("Got CDR end ltag %s end_ts %i.%i\n",
|
|
// args[CC_API_PARAMS_LTAG].asCStr(),
|
|
// args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_END_SEC].asInt(),
|
|
// args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_END_USEC].asInt());
|
|
// INFO("--------------------------------------------------------------\n");
|
|
|
|
// cc_name, ltag, call_profile, end_ts_sec, end_ts_usec
|
|
// args.assertArrayFmt("ssoa");
|
|
// 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,
|
|
args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_END_SEC].asInt(),
|
|
args[CC_API_PARAMS_TIMESTAMPS][CC_API_TS_END_USEC].asInt()
|
|
);
|
|
} else if(method == "_list"){
|
|
ret.push("start");
|
|
ret.push("connect");
|
|
ret.push("end");
|
|
}
|
|
else
|
|
throw AmDynInvoke::NotImplemented(method);
|
|
}
|
|
|
|
static RestParams::Format getFormat(const AmArg &values, RestParams::Format _default)
|
|
{
|
|
if (!values.hasMember("format")) return _default;
|
|
|
|
const AmArg &a = values["format"];
|
|
if (!isArgCStr(a)) throw string("configuration error: wrong arg type\n");
|
|
|
|
const char *str = a.asCStr();
|
|
if (str) {
|
|
if (strcmp(str, "text") == 0) return RestParams::TEXT;
|
|
if (strcmp(str, "json") == 0) return RestParams::JSON;
|
|
if (strcmp(str, "xml") == 0) return RestParams::XML;
|
|
}
|
|
|
|
throw string("invalid format parameter value\n");
|
|
|
|
return _default;
|
|
}
|
|
|
|
static void setHeaderFilter(SBCCallProfile* call_profile,
|
|
const string &type, const string &list)
|
|
{
|
|
call_profile->headerfilter.push_back(FilterEntry());
|
|
|
|
if (type=="transparent")
|
|
call_profile->headerfilter.back().filter_type = Transparent;
|
|
else if (type=="whitelist")
|
|
call_profile->headerfilter.back().filter_type = Whitelist;
|
|
else if (type=="blacklist")
|
|
call_profile->headerfilter.back().filter_type = Blacklist;
|
|
else {
|
|
ERROR("invalid header_filter mode '%s'\n", type.c_str());
|
|
throw string("invalid header filter");
|
|
}
|
|
|
|
vector<string> v = explode(list, ",");
|
|
for (vector<string>::iterator i = v.begin(); i != v.end(); ++i) {
|
|
transform(i->begin(), i->end(), i->begin(), ::tolower);
|
|
call_profile->headerfilter.back().filter_list.insert(*i);
|
|
}
|
|
}
|
|
|
|
void RestModule::start(const string& cc_name, const string& ltag,
|
|
SBCCallProfile* call_profile,
|
|
int start_ts_sec, int start_ts_usec,
|
|
const AmArg& values, int timer_id, AmArg& res) {
|
|
res.push(AmArg());
|
|
AmArg& res_cmd = res[0];
|
|
|
|
try {
|
|
string url;
|
|
|
|
if (!values.hasMember("url"))
|
|
throw string("configuration error: url must be configured for REST queries\n");
|
|
|
|
if (!isArgCStr(values["url"]) || !strlen(values["url"].asCStr())) {
|
|
throw string("configuration error: invalid value of url\n");
|
|
}
|
|
|
|
url = values["url"].asCStr();
|
|
|
|
RestParams params;
|
|
if (params.retrieve(url, getFormat(values, RestParams::TEXT))) {
|
|
// parameters successfully read from server
|
|
|
|
params.getIfSet("ruri", call_profile->ruri);
|
|
params.getIfSet("from", call_profile->from);
|
|
params.getIfSet("to", call_profile->to);
|
|
//TODO: params.getIfSet("contact", call_profile->contact);
|
|
params.getIfSet("call-id", call_profile->callid);
|
|
params.getIfSet("outbound_proxy", call_profile->outbound_proxy);
|
|
params.getIfSet("force_outbound_proxy", call_profile->force_outbound_proxy);
|
|
params.getIfSet("next_hop", call_profile->next_hop);
|
|
|
|
string hf_type, hf_list;
|
|
params.getIfSet("header_filter", hf_type);
|
|
params.getIfSet("header_list", hf_list);
|
|
if ( (!hf_type.empty()) || (!hf_list.empty()))
|
|
setHeaderFilter(call_profile, hf_type, hf_list);
|
|
|
|
//messagefilter
|
|
|
|
// sdpfilter, anonymize_sdp
|
|
|
|
params.getIfSet("sst_enabled", call_profile->sst_enabled);
|
|
//doesn't work:params.getIfSet("sst_aleg_enabled", call_profile->sst_aleg_enabled);
|
|
|
|
// TODO: autho, auth_aleg, reply translations
|
|
|
|
params.getIfSet("append_headers", call_profile->append_headers); // CRLF is handled in SBC
|
|
|
|
//doesn't work: params.getIfSet("refuse_with", call_profile->refuse_with);
|
|
|
|
// TODO: rtprelay, symmetric_rtp, ...
|
|
params.getIfSet("rtprelay_interface", call_profile->rtprelay_interface);
|
|
params.getIfSet("aleg_rtprelay_interface", call_profile->aleg_rtprelay_interface);
|
|
|
|
params.getIfSet("outbound_interface", call_profile->outbound_interface);
|
|
}
|
|
}
|
|
catch (string &err) {
|
|
ERROR("%s", err.c_str());
|
|
res_cmd[SBC_CC_ACTION] = SBC_CC_REFUSE_ACTION;
|
|
res_cmd[SBC_CC_REFUSE_CODE] = 500;
|
|
res_cmd[SBC_CC_REFUSE_REASON] = "REST configuration error";
|
|
}
|
|
}
|
|
|
|
void RestModule::connect(const string& cc_name, const string& ltag,
|
|
SBCCallProfile* call_profile,
|
|
const string& other_tag,
|
|
int connect_ts_sec, int connect_ts_usec) {
|
|
// connect code here
|
|
|
|
}
|
|
|
|
void RestModule::end(const string& cc_name, const string& ltag,
|
|
SBCCallProfile* call_profile,
|
|
int end_ts_sec, int end_ts_usec) {
|
|
// end code here
|
|
|
|
}
|