Merge branch 'master' of ssh://git.sip-router.org/sems

sayer/1.4-spce2.6
bpintea 15 years ago
commit d4a17dd659

@ -534,6 +534,10 @@ int IvrDialog::transfer(const string& target)
return dlg.transfer(target);
}
int IvrDialog::refer(const string& target, int expires) {
return dlg.refer(target, expires);
}
int IvrDialog::drop()
{
int res = dlg.drop();

@ -141,6 +141,7 @@ class IvrDialog : public AmB2BCallerSession
void setPyPtrs(PyObject *mod, PyObject *dlg);
int transfer(const string& target);
int refer(const string& target, int expires);
int drop();
void onSessionStart(const AmSipRequest& req);

@ -495,6 +495,26 @@ IvrDialogBase_redirect(IvrDialogBase *self, PyObject* args)
}
static PyObject*
IvrDialogBase_refer(IvrDialogBase *self, PyObject* args)
{
assert(self->p_dlg);
char* refer_to=0;
int expires;
if(!PyArg_ParseTuple(args,"si",&refer_to, &expires))
return NULL;
if(self->p_dlg->refer(refer_to, expires)){
ERROR("REFER failed\n");
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}
static PyMethodDef IvrDialogBase_methods[] = {
@ -528,6 +548,9 @@ static PyMethodDef IvrDialogBase_methods[] = {
"Send a BYE"
},
{"redirect", (PyCFunction)IvrDialogBase_redirect, METH_VARARGS,
"Transfers the remote party to some third party."
},
{"refer", (PyCFunction)IvrDialogBase_refer, METH_VARARGS,
"Refers the remote party to some third party."
},
{"dropSession", (PyCFunction)IvrDialogBase_dropSession, METH_NOARGS,

@ -2,6 +2,8 @@
# authentication mode:
# XMLRPC : authenticate against XMLRPC server
# REFER : add pin to REFER sent out to be checked at proxy
# TRANSFER : add pin to R-URI, transfer call flow (see Readme.pin_collect.txt)
auth_mode=XMLRPC
# XMLRPC url to authenticate against if auth_mode==XMLRPC

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from log import *
from ivr import *
@ -101,6 +102,12 @@ class IvrDialog(IvrDialogBase):
self.fail_msg.open(config['fail_msg'],AUDIO_READ)
self.enqueue(self.fail_msg,None)
elif config['auth_mode'] == 'REFER':
self.state = connect
self.removeTimer(HINT_TIMER)
self.transfer_cseq = self.dialog.cseq
self.refer("sip:" + self.dialog.user + "+" + self.keys + "@" + \
self.dialog.domain, 20)
else:
self.state = connect
self.removeTimer(HINT_TIMER)

@ -1,5 +1,6 @@
set (sbc_SRCS
SBC.cpp
SBCCallProfile.cpp
HeaderFilter.cpp
ParamReplacer.cpp
SDPFilter.cpp

@ -52,7 +52,6 @@ SBC - feature-wishlist
#include "ParamReplacer.h"
#include "SDPFilter.h"
#include <algorithm>
using std::map;
string SBCFactory::user;
@ -64,186 +63,6 @@ AmSessionEventHandlerFactory* SBCFactory::session_timer_fact = NULL;
EXPORT_SESSION_FACTORY(SBCFactory,MOD_NAME);
bool SBCCallProfile::readFromConfiguration(const string& name,
const string profile_file_name) {
if (cfg.loadFile(profile_file_name)) {
ERROR("reading SBC call profile from '%s'\n", profile_file_name.c_str());
return false;
}
ruri = cfg.getParameter("RURI");
from = cfg.getParameter("From");
to = cfg.getParameter("To");
callid = cfg.getParameter("Call-ID");
force_outbound_proxy = cfg.getParameter("force_outbound_proxy") == "yes";
outbound_proxy = cfg.getParameter("outbound_proxy");
next_hop_ip = cfg.getParameter("next_hop_ip");
next_hop_port = cfg.getParameter("next_hop_port");
string hf_type = cfg.getParameter("header_filter", "transparent");
if (hf_type=="transparent")
headerfilter = Transparent;
else if (hf_type=="whitelist")
headerfilter = Whitelist;
else if (hf_type=="blacklist")
headerfilter = Blacklist;
else {
ERROR("invalid header_filter mode '%s'\n", hf_type.c_str());
return false;
}
vector<string> elems = explode(cfg.getParameter("header_list"), ",");
for (vector<string>::iterator it=elems.begin(); it != elems.end(); it++)
headerfilter_list.insert(*it);
string mf_type = cfg.getParameter("message_filter", "transparent");
if (mf_type=="transparent")
messagefilter = Transparent;
else if (mf_type=="whitelist")
messagefilter = Whitelist;
else if (hf_type=="blacklist")
messagefilter = Blacklist;
else {
ERROR("invalid message_filter mode '%s'\n", mf_type.c_str());
return false;
}
elems = explode(cfg.getParameter("message_list"), ",");
for (vector<string>::iterator it=elems.begin(); it != elems.end(); it++)
messagefilter_list.insert(*it);
string sdp_filter = cfg.getParameter("sdp_filter");
if (sdp_filter=="transparent") {
sdpfilter_enabled = true;
sdpfilter = Transparent;
} else if (sdp_filter=="whitelist") {
sdpfilter_enabled = true;
sdpfilter = Whitelist;
} else if (sdp_filter=="blacklist") {
sdpfilter_enabled = true;
sdpfilter = Blacklist;
} else {
sdpfilter_enabled = false;
}
if (sdpfilter_enabled) {
vector<string> c_elems = explode(cfg.getParameter("sdpfilter_list"), ",");
for (vector<string>::iterator it=c_elems.begin(); it != c_elems.end(); it++) {
string c = *it;
std::transform(c.begin(), c.end(), c.begin(), ::tolower);
sdpfilter_list.insert(c);
}
}
sst_enabled = cfg.getParameter("enable_session_timer", "no") == "yes";
use_global_sst_config = !cfg.hasParameter("session_expires");
auth_enabled = cfg.getParameter("enable_auth", "no") == "yes";
auth_credentials.user = cfg.getParameter("auth_user");
auth_credentials.pwd = cfg.getParameter("auth_pwd");
call_timer_enabled = cfg.getParameter("enable_call_timer", "no") == "yes";
call_timer = cfg.getParameter("call_timer");
prepaid_enabled = cfg.getParameter("enable_prepaid", "no") == "yes";
prepaid_accmodule = cfg.getParameter("prepaid_accmodule");
prepaid_uuid = cfg.getParameter("prepaid_uuid");
prepaid_acc_dest = cfg.getParameter("prepaid_acc_dest");
// check for acc module if configured statically
if (prepaid_enabled &&
(prepaid_accmodule.find('$') == string::npos) &&
(NULL == AmPlugIn::instance()->getFactory4Di(prepaid_accmodule))) {
ERROR("prepaid accounting module '%s' used in call profile "
"'%s' is not loaded\n", prepaid_accmodule.c_str(), name.c_str());
return false;
}
vector<string> reply_translations_v =
explode(cfg.getParameter("reply_translations"), "|");
for (vector<string>::iterator it =
reply_translations_v.begin(); it != reply_translations_v.end(); it++) {
// expected: "603=>488 Not acceptable here"
vector<string> trans_components = explode(*it, "=>");
if (trans_components.size() != 2) {
ERROR("%s: entry '%s' in reply_translations could not be understood.\n",
name.c_str(), it->c_str());
ERROR("expected 'from_code=>to_code reason'\n");
return false;
}
unsigned int from_code, to_code;
if (str2i(trans_components[0], from_code)) {
ERROR("%s: code '%s' in reply_translations not understood.\n",
name.c_str(), trans_components[0].c_str());
return false;
}
unsigned int s_pos = 0;
string to_reply = trans_components[1];
while (s_pos < to_reply.length() && to_reply[s_pos] != ' ')
s_pos++;
if (str2i(to_reply.substr(0, s_pos), to_code)) {
ERROR("%s: code '%s' in reply_translations not understood.\n",
name.c_str(), to_reply.substr(0, s_pos).c_str());
return false;
}
if (s_pos < to_reply.length())
s_pos++;
// DBG("got translation %u => %u %s\n",
// from_code, to_code, to_reply.substr(s_pos).c_str());
reply_translations[from_code] = make_pair(to_code, to_reply.substr(s_pos));
}
INFO("SBC: loaded SBC profile '%s':\n", name.c_str());
INFO("SBC: RURI = '%s'\n", ruri.c_str());
INFO("SBC: From = '%s'\n", from.c_str());
INFO("SBC: To = '%s'\n", to.c_str());
if (!callid.empty()) {
INFO("SBC: Call-ID = '%s'\n", callid.c_str());
}
INFO("SBC: force outbound proxy: %s\n", force_outbound_proxy?"yes":"no");
INFO("SBC: outbound proxy = '%s'\n", outbound_proxy.c_str());
if (!next_hop_ip.empty()) {
INFO("SBC: next hop = %s%s\n", next_hop_ip.c_str(),
next_hop_port.empty()? "" : (":"+next_hop_port).c_str());
}
INFO("SBC: header filter is %s, %zd items in list\n",
FilterType2String(headerfilter), headerfilter_list.size());
INFO("SBC: message filter is %s, %zd items in list\n",
FilterType2String(messagefilter), messagefilter_list.size());
INFO("SBC: SDP filter is %sabled, %s, %zd items in list\n",
sdpfilter_enabled?"en":"dis", FilterType2String(sdpfilter),
sdpfilter_list.size());
INFO("SBC: SST %sabled\n", sst_enabled?"en":"dis");
INFO("SBC: SIP auth %sabled\n", auth_enabled?"en":"dis");
INFO("SBC: call timer %sabled\n", call_timer_enabled?"en":"dis");
if (call_timer_enabled) {
INFO("SBC: %s seconds\n", call_timer.c_str());
}
INFO("SBC: prepaid %sabled\n", prepaid_enabled?"en":"dis");
if (prepaid_enabled) {
INFO("SBC: acc_module = '%s'\n", prepaid_accmodule.c_str());
INFO("SBC: uuid = '%s'\n", prepaid_uuid.c_str());
INFO("SBC: acc_dest = '%s'\n", prepaid_acc_dest.c_str());
}
if (reply_translations.size()) {
string reply_trans_codes;
for(map<unsigned int, pair<unsigned int, string> >::iterator it=
reply_translations.begin(); it != reply_translations.end(); it++)
reply_trans_codes += int2str(it->first)+", ";
reply_trans_codes.erase(reply_trans_codes.length()-2);
INFO("SBC: reply translation for %s\n", reply_trans_codes.c_str());
}
return true;
}
SBCFactory::SBCFactory(const string& _app_name)
: AmSessionFactory(_app_name)

@ -27,11 +27,11 @@
#define _SBC_H
#include "AmB2BSession.h"
#include "ampi/UACAuthAPI.h"
#include "AmConfigReader.h"
#include "AmUriParser.h"
#include "HeaderFilter.h"
#include "SBCCallProfile.h"
#include <map>
@ -40,65 +40,6 @@ using std::string;
#define SBC_TIMER_ID_CALL_TIMER 1
#define SBC_TIMER_ID_PREPAID_TIMEOUT 2
struct SBCCallProfile {
AmConfigReader cfg;
string ruri; /* updated if set */
string from; /* updated if set */
string to; /* updated if set */
string callid;
string outbound_proxy;
bool force_outbound_proxy;
string next_hop_ip;
string next_hop_port;
unsigned short next_hop_port_i;
FilterType headerfilter;
set<string> headerfilter_list;
FilterType messagefilter;
set<string> messagefilter_list;
bool sdpfilter_enabled;
FilterType sdpfilter;
set<string> sdpfilter_list;
bool sst_enabled;
bool use_global_sst_config;
bool auth_enabled;
UACAuthCred auth_credentials;
bool call_timer_enabled;
string call_timer;
bool prepaid_enabled;
string prepaid_accmodule;
string prepaid_uuid;
string prepaid_acc_dest;
map<unsigned int, pair<unsigned int, string> > reply_translations;
// todo: RTP forwarding mode
// todo: RTP transcoding mode
SBCCallProfile()
: headerfilter(Transparent),
messagefilter(Transparent),
sdpfilter_enabled(false),
sdpfilter(Transparent),
sst_enabled(false),
auth_enabled(false),
call_timer_enabled(false),
prepaid_enabled(false)
{ }
bool readFromConfiguration(const string& name, const string profile_file_name);
};
class SBCFactory: public AmSessionFactory
{

@ -0,0 +1,189 @@
#include "SBCCallProfile.h"
#include <algorithm>
#include "log.h"
#include "AmUtils.h"
#include "AmPlugIn.h"
bool SBCCallProfile::readFromConfiguration(const string& name,
const string profile_file_name) {
if (cfg.loadFile(profile_file_name)) {
ERROR("reading SBC call profile from '%s'\n", profile_file_name.c_str());
return false;
}
ruri = cfg.getParameter("RURI");
from = cfg.getParameter("From");
to = cfg.getParameter("To");
callid = cfg.getParameter("Call-ID");
force_outbound_proxy = cfg.getParameter("force_outbound_proxy") == "yes";
outbound_proxy = cfg.getParameter("outbound_proxy");
next_hop_ip = cfg.getParameter("next_hop_ip");
next_hop_port = cfg.getParameter("next_hop_port");
string hf_type = cfg.getParameter("header_filter", "transparent");
if (hf_type=="transparent")
headerfilter = Transparent;
else if (hf_type=="whitelist")
headerfilter = Whitelist;
else if (hf_type=="blacklist")
headerfilter = Blacklist;
else {
ERROR("invalid header_filter mode '%s'\n", hf_type.c_str());
return false;
}
vector<string> elems = explode(cfg.getParameter("header_list"), ",");
for (vector<string>::iterator it=elems.begin(); it != elems.end(); it++)
headerfilter_list.insert(*it);
string mf_type = cfg.getParameter("message_filter", "transparent");
if (mf_type=="transparent")
messagefilter = Transparent;
else if (mf_type=="whitelist")
messagefilter = Whitelist;
else if (hf_type=="blacklist")
messagefilter = Blacklist;
else {
ERROR("invalid message_filter mode '%s'\n", mf_type.c_str());
return false;
}
elems = explode(cfg.getParameter("message_list"), ",");
for (vector<string>::iterator it=elems.begin(); it != elems.end(); it++)
messagefilter_list.insert(*it);
string sdp_filter = cfg.getParameter("sdp_filter");
if (sdp_filter=="transparent") {
sdpfilter_enabled = true;
sdpfilter = Transparent;
} else if (sdp_filter=="whitelist") {
sdpfilter_enabled = true;
sdpfilter = Whitelist;
} else if (sdp_filter=="blacklist") {
sdpfilter_enabled = true;
sdpfilter = Blacklist;
} else {
sdpfilter_enabled = false;
}
if (sdpfilter_enabled) {
vector<string> c_elems = explode(cfg.getParameter("sdpfilter_list"), ",");
for (vector<string>::iterator it=c_elems.begin(); it != c_elems.end(); it++) {
string c = *it;
std::transform(c.begin(), c.end(), c.begin(), ::tolower);
sdpfilter_list.insert(c);
}
}
sst_enabled = cfg.getParameter("enable_session_timer", "no") == "yes";
use_global_sst_config = !cfg.hasParameter("session_expires");
auth_enabled = cfg.getParameter("enable_auth", "no") == "yes";
auth_credentials.user = cfg.getParameter("auth_user");
auth_credentials.pwd = cfg.getParameter("auth_pwd");
call_timer_enabled = cfg.getParameter("enable_call_timer", "no") == "yes";
call_timer = cfg.getParameter("call_timer");
prepaid_enabled = cfg.getParameter("enable_prepaid", "no") == "yes";
prepaid_accmodule = cfg.getParameter("prepaid_accmodule");
prepaid_uuid = cfg.getParameter("prepaid_uuid");
prepaid_acc_dest = cfg.getParameter("prepaid_acc_dest");
// check for acc module if configured statically
if (prepaid_enabled &&
(prepaid_accmodule.find('$') == string::npos) &&
(NULL == AmPlugIn::instance()->getFactory4Di(prepaid_accmodule))) {
ERROR("prepaid accounting module '%s' used in call profile "
"'%s' is not loaded\n", prepaid_accmodule.c_str(), name.c_str());
return false;
}
vector<string> reply_translations_v =
explode(cfg.getParameter("reply_translations"), "|");
for (vector<string>::iterator it =
reply_translations_v.begin(); it != reply_translations_v.end(); it++) {
// expected: "603=>488 Not acceptable here"
vector<string> trans_components = explode(*it, "=>");
if (trans_components.size() != 2) {
ERROR("%s: entry '%s' in reply_translations could not be understood.\n",
name.c_str(), it->c_str());
ERROR("expected 'from_code=>to_code reason'\n");
return false;
}
unsigned int from_code, to_code;
if (str2i(trans_components[0], from_code)) {
ERROR("%s: code '%s' in reply_translations not understood.\n",
name.c_str(), trans_components[0].c_str());
return false;
}
unsigned int s_pos = 0;
string to_reply = trans_components[1];
while (s_pos < to_reply.length() && to_reply[s_pos] != ' ')
s_pos++;
if (str2i(to_reply.substr(0, s_pos), to_code)) {
ERROR("%s: code '%s' in reply_translations not understood.\n",
name.c_str(), to_reply.substr(0, s_pos).c_str());
return false;
}
if (s_pos < to_reply.length())
s_pos++;
// DBG("got translation %u => %u %s\n",
// from_code, to_code, to_reply.substr(s_pos).c_str());
reply_translations[from_code] = make_pair(to_code, to_reply.substr(s_pos));
}
INFO("SBC: loaded SBC profile '%s':\n", name.c_str());
INFO("SBC: RURI = '%s'\n", ruri.c_str());
INFO("SBC: From = '%s'\n", from.c_str());
INFO("SBC: To = '%s'\n", to.c_str());
if (!callid.empty()) {
INFO("SBC: Call-ID = '%s'\n", callid.c_str());
}
INFO("SBC: force outbound proxy: %s\n", force_outbound_proxy?"yes":"no");
INFO("SBC: outbound proxy = '%s'\n", outbound_proxy.c_str());
if (!next_hop_ip.empty()) {
INFO("SBC: next hop = %s%s\n", next_hop_ip.c_str(),
next_hop_port.empty()? "" : (":"+next_hop_port).c_str());
}
INFO("SBC: header filter is %s, %zd items in list\n",
FilterType2String(headerfilter), headerfilter_list.size());
INFO("SBC: message filter is %s, %zd items in list\n",
FilterType2String(messagefilter), messagefilter_list.size());
INFO("SBC: SDP filter is %sabled, %s, %zd items in list\n",
sdpfilter_enabled?"en":"dis", FilterType2String(sdpfilter),
sdpfilter_list.size());
INFO("SBC: SST %sabled\n", sst_enabled?"en":"dis");
INFO("SBC: SIP auth %sabled\n", auth_enabled?"en":"dis");
INFO("SBC: call timer %sabled\n", call_timer_enabled?"en":"dis");
if (call_timer_enabled) {
INFO("SBC: %s seconds\n", call_timer.c_str());
}
INFO("SBC: prepaid %sabled\n", prepaid_enabled?"en":"dis");
if (prepaid_enabled) {
INFO("SBC: acc_module = '%s'\n", prepaid_accmodule.c_str());
INFO("SBC: uuid = '%s'\n", prepaid_uuid.c_str());
INFO("SBC: acc_dest = '%s'\n", prepaid_acc_dest.c_str());
}
if (reply_translations.size()) {
string reply_trans_codes;
for(map<unsigned int, std::pair<unsigned int, string> >::iterator it=
reply_translations.begin(); it != reply_translations.end(); it++)
reply_trans_codes += int2str(it->first)+", ";
reply_trans_codes.erase(reply_trans_codes.length()-2);
INFO("SBC: reply translation for %s\n", reply_trans_codes.c_str());
}
return true;
}

@ -0,0 +1,77 @@
#ifndef _SBCCallProfile_h
#define _SBCCallProfile_h
#include "AmConfigReader.h"
#include "HeaderFilter.h"
#include "ampi/UACAuthAPI.h"
#include <set>
#include <string>
#include <map>
using std::string;
using std::map;
using std::set;
struct SBCCallProfile {
AmConfigReader cfg;
string ruri; /* updated if set */
string from; /* updated if set */
string to; /* updated if set */
string callid;
string outbound_proxy;
bool force_outbound_proxy;
string next_hop_ip;
string next_hop_port;
unsigned short next_hop_port_i;
FilterType headerfilter;
set<string> headerfilter_list;
FilterType messagefilter;
set<string> messagefilter_list;
bool sdpfilter_enabled;
FilterType sdpfilter;
set<string> sdpfilter_list;
bool sst_enabled;
bool use_global_sst_config;
bool auth_enabled;
UACAuthCred auth_credentials;
bool call_timer_enabled;
string call_timer;
bool prepaid_enabled;
string prepaid_accmodule;
string prepaid_uuid;
string prepaid_acc_dest;
map<unsigned int, std::pair<unsigned int, string> > reply_translations;
// todo: RTP forwarding mode
// todo: RTP transcoding mode
SBCCallProfile()
: headerfilter(Transparent),
messagefilter(Transparent),
sdpfilter_enabled(false),
sdpfilter(Transparent),
sst_enabled(false),
auth_enabled(false),
call_timer_enabled(false),
prepaid_enabled(false)
{ }
bool readFromConfiguration(const string& name, const string profile_file_name);
};
#endif // _SBCCallProfile_h

@ -743,7 +743,7 @@ int AmSipDialog::transfer(const string& target)
string r_set;
if(!route.empty()){
hdrs = PARAM_HDR ": " "Transfer-RR=\"" + route + "\"";
hdrs = PARAM_HDR ": " "Transfer-RR=\"" + route + "\""+CRLF;
}
int ret = tmp_d.sendRequest("REFER","","",hdrs);

@ -10,11 +10,14 @@ Authentication Modes:
XMLRPC : Authenticate against an XMLRPC server (python example
server in test/authserver.py
REFER : the transfer request (REFER) sent out has as user part of
REFER : The Refer-to of the REFER sent in-dialog contains <user>+<pin>@domain,
such that this pin can be checked by an upstream app server or
proxy and acted upon (e.g. sent to the proper conference room).
TRANSFER : the transfer request (Transfer REFER) sent out has as user part of
the URI the original user part, a plus sign, and the entered
PIN. The PIN can thus be verified by the proxy handling the
transfer REFER.
transfer REFER. See below for an explanation.
"Transfer" REFER:
The "Transfer REFER" is a proprietary REFER call flow which transfers a

@ -173,7 +173,7 @@ set to transparent, the SDP is parsed and reconstructed (SDP sanity check).
Codecs may be filtered out by their payload names in whitelist or blacklist
modes. The payload names in the list are case-insensitive (PCMU==pcmu).
Reply code translations
Response code translations
-----------------------
Response codes and reasons may be translated, e.g. if some 6xx class replies need
to be changed to 4xx class replies.
@ -256,6 +256,7 @@ Example profiles
call_timer - call timer (obsoletes call_timer app)
prepaid - prepaid accounting (obsoletes sw_prepaid_sip app)
codecfilter - let only some low bitrate codecs pass
replytranslate - swap 603 and 488 response code in replies
Dependencies
------------
@ -266,7 +267,7 @@ Roadmap
-------
x header filter (whitelist or blacklist)
x message filter (whitelist or blacklist)
- SDP filter (reconstructed SDP)
x SDP filter (reconstructed SDP)
x remote URI update (host / user / host/user)
x From update (displayname / host / host/user)
x To update (displayname / host / host/user)

Loading…
Cancel
Save