From 8a9bca719acc2cf12d810f065b4d8a445d5c7f45 Mon Sep 17 00:00:00 2001 From: Stefan Sayer Date: Tue, 11 Jan 2011 01:09:18 +0100 Subject: [PATCH] sbc: regex maps, flexible active profile selection - regex maps are files with regex=>value entries which, once loaded, can be used with $M(key=>mapname) pattern replacement - it is now possible to use replacements in active_profile, and also a list of profiles can be specified here - the first matching will be used --- apps/sbc/ParamReplacer.cpp | 49 +++++++ apps/sbc/RegexMapper.cpp | 65 ++++++++++ apps/sbc/RegexMapper.h | 55 ++++++++ apps/sbc/SBC.cpp | 137 ++++++++++++++++---- apps/sbc/SBC.h | 13 +- apps/sbc/etc/sbc.conf | 12 +- apps/sbc/etc/src_ipmap.conf | 7 + apps/sbc/tools/sems-sbc-get-regex-map-names | 7 + apps/sbc/tools/sems-sbc-set-regex-map | 13 ++ core/AmUtils.cpp | 22 +++- core/AmUtils.h | 3 + doc/Readme.sbc.txt | 75 +++++++++-- 12 files changed, 420 insertions(+), 38 deletions(-) create mode 100644 apps/sbc/RegexMapper.cpp create mode 100644 apps/sbc/RegexMapper.h create mode 100644 apps/sbc/etc/src_ipmap.conf create mode 100755 apps/sbc/tools/sems-sbc-get-regex-map-names create mode 100755 apps/sbc/tools/sems-sbc-set-regex-map diff --git a/apps/sbc/ParamReplacer.cpp b/apps/sbc/ParamReplacer.cpp index 13dfe6ad..628f36e1 100644 --- a/apps/sbc/ParamReplacer.cpp +++ b/apps/sbc/ParamReplacer.cpp @@ -28,6 +28,8 @@ #include "log.h" #include "AmSipHeaders.h" #include "AmUtils.h" +#include "SBC.h" // for RegexMapper SBCFactory::regex_mappings + void replaceParsedParam(const string& s, size_t p, AmUriParser& parsed, string& res) { @@ -273,6 +275,53 @@ string replaceParameters(const string& s, skip_chars = skip_p-p; } break; + case 'M': { // regex map + if (s[p+1] != '(') { + WARN("Error parsing $M regex map replacement (missing '(')\n"); + break; + } + if (s.length()"); + if (spos == string::npos) { + skip_chars = skip_p-p; + WARN("Error parsing $M regex map replacement: no => found in '%s'\n", + map_str.c_str()); + break; + } + + string map_val = map_str.substr(0, spos); + string map_val_replaced = replaceParameters(map_val, r_type, req, app_param, + ruri_parser, from_parser, to_parser); + string mapping_name = map_str.substr(spos+2); + + string map_res; + if (SBCFactory::regex_mappings. + mapRegex(mapping_name, map_val_replaced.c_str(), map_res)) { + DBG("matched regex mapping '%s' (orig '%s) in '%s'\n", + map_val_replaced.c_str(), map_val.c_str(), mapping_name.c_str()); + res+=map_res; + } else { + DBG("no match in regex mapping '%s' (orig '%s') in '%s'\n", + map_val_replaced.c_str(), map_val.c_str(), mapping_name.c_str()); + } + + skip_chars = skip_p-p; + } break; + default: { WARN("unknown replace pattern $%c%c\n", s[p], s[p+1]); diff --git a/apps/sbc/RegexMapper.cpp b/apps/sbc/RegexMapper.cpp new file mode 100644 index 00000000..8ca93e88 --- /dev/null +++ b/apps/sbc/RegexMapper.cpp @@ -0,0 +1,65 @@ +/* + * 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 "RegexMapper.h" +#include "log.h" + +bool RegexMapper::mapRegex(const string& mapping_name, const char* test_s, + string& result) { + lock(); + std::map::iterator it=regex_mappings.find(mapping_name); + if (it == regex_mappings.end()) { + unlock(); + ERROR("regex mapping '%s' is not loaded!\n", mapping_name.c_str()); + return false; + } + + bool res = run_regex_mapping(it->second, test_s, result); + unlock(); + return res; +} + +void RegexMapper::setRegexMap(const string& mapping_name, const RegexMappingVector& r) { + lock(); + std::map::iterator it=regex_mappings.find(mapping_name); + if (it != regex_mappings.end()) { + for (RegexMappingVector::iterator r_it = + it->second.begin(); r_it != it->second.end(); r_it++) { + regfree(&r_it->first); + } + } + regex_mappings[mapping_name] = r; + unlock(); +} + +std::vector RegexMapper::getNames() { + std::vector res; + lock(); + for (std::map::iterator it= + regex_mappings.begin(); it != regex_mappings.end(); it++) + res.push_back(it->first); + unlock(); + return res; +} + diff --git a/apps/sbc/RegexMapper.h b/apps/sbc/RegexMapper.h new file mode 100644 index 00000000..f35a0055 --- /dev/null +++ b/apps/sbc/RegexMapper.h @@ -0,0 +1,55 @@ +/* + * 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 + */ + +#ifndef _RegexMapper_h_ +#define _RegexMapper_h_ + +#include "AmUtils.h" + +#include +#include +#include +#include "AmThread.h" + +struct RegexMapper { + + RegexMapper() { } + ~RegexMapper() { } + + std::map regex_mappings; + AmMutex regex_mappings_mut; + + void lock() { regex_mappings_mut.lock(); } + void unlock() { regex_mappings_mut.unlock(); } + + bool mapRegex(const string& mapping_name, const char* test_s, + string& result); + + void setRegexMap(const string& mapping_name, const RegexMappingVector& r); + + std::vector getNames(); +}; + +#endif diff --git a/apps/sbc/SBC.cpp b/apps/sbc/SBC.cpp index 5ed9d2eb..2bbd6247 100644 --- a/apps/sbc/SBC.cpp +++ b/apps/sbc/SBC.cpp @@ -52,6 +52,7 @@ using std::map; AmConfigReader SBCFactory::cfg; AmSessionEventHandlerFactory* SBCFactory::session_timer_fact = NULL; +RegexMapper SBCFactory::regex_mappings; EXPORT_MODULE_FACTORY(SBCFactory); DEFINE_MODULE_INSTANCE(SBCFactory, MOD_NAME); @@ -89,15 +90,40 @@ int SBCFactory::onLoad() } } - active_profile = cfg.getParameter("active_profile"); - if (active_profile != "$(paramhdr)" && - active_profile != "$(ruri.user)" && - call_profiles.find(active_profile) == call_profiles.end()) { - ERROR("call profile active_profile '%s' not loaded!\n", active_profile.c_str()); + active_profile = explode(cfg.getParameter("active_profile"), ","); + if (active_profile.empty()) { + ERROR("active_profile not set.\n"); return -1; } - INFO("SBC: active profile: '%s'\n", active_profile.c_str()); + string active_profile_s; + for (vector::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+","; + } + active_profile_s.erase(active_profile_s.length()); + + INFO("SBC: active profile: '%s'\n", active_profile_s.c_str()); + + vector regex_maps = explode(cfg.getParameter("regex_maps"), ","); + for (vector::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()); + } if (!AmPlugIn::registerApplication(MOD_NAME, this)) { ERROR("registering "MOD_NAME" application\n"); @@ -112,35 +138,58 @@ int SBCFactory::onLoad() return 0; } +#define REPLACE_VALS req, app_param, ruri_parser, from_parser, to_parser + +/** get the first matching profile name from active profiles */ +string SBCFactory::getActiveProfileMatch(string& profile_rule, const AmSipRequest& req, + const string& app_param, AmUriParser& ruri_parser, + AmUriParser& from_parser, AmUriParser& to_parser) { + string res; + for (vector::iterator it= + active_profile.begin(); it != active_profile.end(); it++) { + if (it->empty()) + continue; + + if (*it == "$(paramhdr)") + res = get_header_keyvalue(app_param,"profile"); + else if (*it == "$(ruri.user)") + res = req.user; + else + res = replaceParameters(*it, "active_profile", REPLACE_VALS); + + if (!res.empty()) { + profile_rule = *it; + break; + } + } + return res; +} AmSession* SBCFactory::onInvite(const AmSipRequest& req) { + AmUriParser ruri_parser, from_parser, to_parser; + profiles_mut.lock(); string app_param = getHeader(req.hdrs, PARAM_HDR, true); - - string profile = active_profile; - if (profile == "$(paramhdr)") { - profile = get_header_keyvalue(app_param,"profile"); - } else if (profile == "$(ruri.user)") { - profile = req.user; - } + + string profile_rule; + string profile = getActiveProfileMatch(profile_rule, REPLACE_VALS); map::iterator it= call_profiles.find(profile); if (it==call_profiles.end()) { profiles_mut.unlock(); - ERROR("could not find call profile '%s' (active_profile = %s)\n", - profile.c_str(), active_profile.c_str()); + ERROR("could not find call profile '%s' (matching active_profile rule: '%s')\n", + profile.c_str(), profile_rule.c_str()); throw AmSession::Exception(500,SIP_REPLY_SERVER_INTERNAL_ERROR); } - DBG("using call profile '%s'\n", profile.c_str()); + DBG("using call profile '%s' (from matching active_profile rule '%s')\n", + profile.c_str(), profile_rule.c_str()); SBCCallProfile& call_profile = it->second; if (!call_profile.refuse_with.empty()) { - AmUriParser ruri_parser, from_parser, to_parser; -#define REPLACE_VALS req, app_param, ruri_parser, from_parser, to_parser string refuse_with = replaceParameters(call_profile.refuse_with, "refuse_with", REPLACE_VALS); @@ -174,7 +223,6 @@ AmSession* SBCFactory::onInvite(const AmSipRequest& req) AmSipDialog::reply_error(req, refuse_with_code, refuse_with_reason, hdrs); return NULL; -#undef REPLACE_VALS } AmConfigReader& sst_cfg = call_profile.use_global_sst_config ? @@ -234,6 +282,11 @@ void SBCFactory::invoke(const string& method, const AmArg& args, } 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 == "_list"){ ret.push(AmArg("listProfiles")); ret.push(AmArg("reloadProfiles")); @@ -241,6 +294,8 @@ void SBCFactory::invoke(const string& method, const AmArg& args, ret.push(AmArg("loadProfile")); ret.push(AmArg("getActiveProfile")); ret.push(AmArg("setActiveProfile")); + ret.push(AmArg("getRegexMapNames")); + ret.push(AmArg("setRegexMap")); } else throw AmDynInvoke::NotImplemented(method); } @@ -364,7 +419,10 @@ void SBCFactory::loadProfile(const AmArg& args, AmArg& ret) { void SBCFactory::getActiveProfile(const AmArg& args, AmArg& ret) { profiles_mut.lock(); AmArg p; - p["active_profile"] = active_profile; + for (vector::iterator it=active_profile.begin(); + it != active_profile.end(); it++) { + p["active_profile"].push(*it); + } profiles_mut.unlock(); ret.push(200); ret.push("OK"); @@ -374,11 +432,11 @@ void SBCFactory::getActiveProfile(const AmArg& args, AmArg& ret) { void SBCFactory::setActiveProfile(const AmArg& args, AmArg& ret) { if (!args[0].hasMember("active_profile")) { ret.push(400); - ret.push("Parameters error: expected ['active_profile': ] "); + ret.push("Parameters error: expected ['active_profile': ] "); return; } profiles_mut.lock(); - active_profile = args[0]["active_profile"].asCStr(); + active_profile = explode(args[0]["active_profile"].asCStr(), ","); profiles_mut.unlock(); ret.push(200); ret.push("OK"); @@ -387,6 +445,39 @@ void SBCFactory::setActiveProfile(const AmArg& args, AmArg& ret) { ret.push(p); } +void SBCFactory::getRegexMapNames(const AmArg& args, AmArg& ret) { + AmArg p; + vector reg_names = regex_mappings.getNames(); + for (vector::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': , 'file': ]"); + 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"); +} SBCDialog::SBCDialog(const SBCCallProfile& call_profile) : m_state(BB_Init), @@ -408,8 +499,6 @@ void SBCDialog::onInvite(const AmSipRequest& req) DBG("processing initial INVITE\n"); -#define REPLACE_VALS req, app_param, ruri_parser, from_parser, to_parser - string app_param = getHeader(req.hdrs, PARAM_HDR, true); if(dlg.reply(req, 100, "Connecting") != 0) { diff --git a/apps/sbc/SBC.h b/apps/sbc/SBC.h index f38a1f0f..484ee4fb 100644 --- a/apps/sbc/SBC.h +++ b/apps/sbc/SBC.h @@ -32,6 +32,7 @@ #include "AmUriParser.h" #include "HeaderFilter.h" #include "SBCCallProfile.h" +#include "RegexMapper.h" #include @@ -48,7 +49,7 @@ class SBCFactory: public AmSessionFactory, std::map call_profiles; - string active_profile; + vector active_profile; AmMutex profiles_mut; void listProfiles(const AmArg& args, AmArg& ret); @@ -57,6 +58,12 @@ class SBCFactory: public AmSessionFactory, void loadProfile(const AmArg& args, AmArg& ret); void getActiveProfile(const AmArg& args, AmArg& ret); void setActiveProfile(const AmArg& args, AmArg& ret); + void getRegexMapNames(const AmArg& args, AmArg& ret); + void setRegexMap(const AmArg& args, AmArg& ret); + + string getActiveProfileMatch(string& profile_rule, const AmSipRequest& req, + const string& app_param, AmUriParser& ruri_parser, + AmUriParser& from_parser, AmUriParser& to_parser); public: DECLARE_MODULE_INSTANCE(SBCFactory); @@ -70,6 +77,8 @@ class SBCFactory: public AmSessionFactory, static AmConfigReader cfg; static AmSessionEventHandlerFactory* session_timer_fact; + static RegexMapper regex_mappings; + // DI // DI factory AmDynInvoke* getInstance() { return instance(); } @@ -166,6 +175,6 @@ class SBCCalleeSession void setAuthHandler(AmSessionEventHandler* h) { auth = h; } }; -static void assertEndCRLF(string& s); +extern void assertEndCRLF(string& s); #endif diff --git a/apps/sbc/etc/sbc.conf b/apps/sbc/etc/sbc.conf index 50b1574b..f78f80d3 100644 --- a/apps/sbc/etc/sbc.conf +++ b/apps/sbc/etc/sbc.conf @@ -5,7 +5,7 @@ # path (the path where this file resides) profiles=transparent,auth_b2b,sst_b2b -# active call profile +# active call profile - comma separated list, first non-empty is used # # o active_profile= always use # @@ -13,8 +13,16 @@ profiles=transparent,auth_b2b,sst_b2b # # o active_profile=$(paramhdr) use "profile" option in P-App-Param header # +# o any replacement pattern +# active_profile=transparent +# regex_maps - comma-separated list of regex maps to load at startup, for $M() +# +# regex=>value maps for which names are given here are loaded from +# this path, e.g. src_ipmap.conf, ruri_map.conf, usermap.conf +# +#regex_maps=src_ipmap,ruri_map,usermap ## RFC4028 Session Timer # default configuration - can be overridden by call profiles @@ -39,8 +47,6 @@ active_profile=transparent # # Default: UPDATE_FALLBACK_INVITE # -# Note: Session Timers are only supported in some applications -# #session_refresh_method=UPDATE # accept_501_reply - accept 501 reply as successful refresh? [yes|no] diff --git a/apps/sbc/etc/src_ipmap.conf b/apps/sbc/etc/src_ipmap.conf new file mode 100644 index 00000000..a7edbe5a --- /dev/null +++ b/apps/sbc/etc/src_ipmap.conf @@ -0,0 +1,7 @@ +# this is a sample regex mapping to map source address 10.0.* to internal1 profile +# and 10.1.* to internal2 profile. +# For example, used with this active_profile setting: +# active_profile=$M($si=>src_ipmap),refuse +# all other calls would be blocked. +^10\.0\..*=>internal1 +^10\.1\..*=>internal2 diff --git a/apps/sbc/tools/sems-sbc-get-regex-map-names b/apps/sbc/tools/sems-sbc-get-regex-map-names new file mode 100755 index 00000000..9bb896c1 --- /dev/null +++ b/apps/sbc/tools/sems-sbc-get-regex-map-names @@ -0,0 +1,7 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +import sys +from xmlrpclib import * + +s = ServerProxy('http://localhost:8090') +print s.getRegexMapNames() diff --git a/apps/sbc/tools/sems-sbc-set-regex-map b/apps/sbc/tools/sems-sbc-set-regex-map new file mode 100755 index 00000000..3581332b --- /dev/null +++ b/apps/sbc/tools/sems-sbc-set-regex-map @@ -0,0 +1,13 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +import sys +from xmlrpclib import * + +if len(sys.argv) != 3: + print "usage: %s " % sys.argv[0] + sys.exit(1) + +s = ServerProxy('http://localhost:8090') +print "Active calls: %d" % s.calls() +p ={ 'name' : sys.argv[1], 'file' : sys.argv[2] } +print s.setRegexMap(p) diff --git a/core/AmUtils.cpp b/core/AmUtils.cpp index 02b45d83..d8113a33 100644 --- a/core/AmUtils.cpp +++ b/core/AmUtils.cpp @@ -951,7 +951,7 @@ string get_header_keyvalue(const string& param_hdr, const string& short_name, co } /** - * get value from parameter header with the name @param name + * get value from parameter header with the name @param name * while skipping escaped values */ string get_header_keyvalue(const string& param_hdr, const string& name) { @@ -1217,6 +1217,26 @@ void add_env_path(const char* name, const string& path) #endif } +/** skip to the end of a string enclosed in round brackets, skipping more + bracketed items, too */ +size_t skip_to_end_of_brackets(const string& s, size_t start) { + size_t res = start; + char last_c = ' '; + int num_brackets = 0; + while (res < s.size() && + (s[res] != ')' || num_brackets || last_c == '\\')) { + if (last_c != '\\') { + if (s[res]==')' && num_brackets) + num_brackets--; + else if (s[res]=='(') + num_brackets++; + } + last_c = s[res]; + res++; + } + return res; +} + bool read_regex_mapping(const string& fname, const char* separator, const char* dbg_type, RegexMappingVector& result) { diff --git a/core/AmUtils.h b/core/AmUtils.h index d17a0446..6406a15d 100644 --- a/core/AmUtils.h +++ b/core/AmUtils.h @@ -343,6 +343,9 @@ std::vector explode(const string& s, const string& delim, const bool kee /** add a directory to an environement variable */ void add_env_path(const char* name, const string& path); + +size_t skip_to_end_of_brackets(const string& s, size_t start); + typedef std::vector > RegexMappingVector; /** read a regex=>string mapping from file diff --git a/doc/Readme.sbc.txt b/doc/Readme.sbc.txt index 3992d5f2..887a8953 100644 --- a/doc/Readme.sbc.txt +++ b/doc/Readme.sbc.txt @@ -29,16 +29,46 @@ SBC Profiles All features are set in an SBC profile, which is configured in a separate configuration file with the extension .sbcprofile.conf. Several SBC profiles may be loaded at startup (load_profiles), and can be selected with the -active_profile configuration option +active_profile configuration option. The active_profile option is a comma-separated +list, the first profile that matches, i.e. is non-empty, will be used. + +In this list a profile may be selected o statically (active_profile=) - o depending on user part of INVITE Request URI(active_profile=$(ruri.user)) + o depending on user part of INVITE Request URI (active_profile=$(ruri.user)) o depending on "profile" option in P-App-Param header (active_profile=$(paramhdr)) -By using the latter two options, the SBC profile for the call can be selected in the -proxy. + o using any replacement pattern (see below), especially regex maps $M(val=>map) + +By using the latter options, the SBC profile for the call can also be selected in +the proxy. + +Examples: + active_profile=auth_b2b + active_profile=$(paramhdr),refuse + active_profile=$M($si=>ipmap),$(P-SBCProfile),refuse + +Example: + In order to have all calls coming from source IP 10.0.* going to + 'internal1' profile, all calls coming from source IP 10.1.* going to 'internal2' + profile, then for calls coming from other IP addresses those to RURI-domain + iptel.org go to 'iptel' profile, and all other calls being refused, we could set + ~~~~~~~~~ sbc.conf ~~~~~~~~~ + profiles=internal1,internal2,iptel,refuse + regex_maps=src_ipmap,rurimap + active_profile=$M($si=>src_ipmap),$M($rh=>rurimap),refuse + ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~ + + ~~~~~~~~~ src_ipmap.conf ~~~ + ^10\.0\..*=>internal1 + ^10\.1\..*=>internal2 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + ~~~~~~~~~ rurimap.conf ~~~~~ + iptel.org=>iptel + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SBC profile reload ------------------ @@ -65,11 +95,11 @@ is loaded. The expected parameters to all functions are in a dictionary with 'name' : profile name 'path' : profile conf file path 'active_profile': active profile (string) -Return code is [200, "OK", ] on success, or [, ] -on failure. +Return code is [200, "OK", ] on success, or +[, ] on failure. -RURI, From, To, etc - Replacement patterns -------------------------------------- +Replacement patterns - active_profile, RURI, From, To, etc +---------------------------------------------------------- In SBC profile the appearance of the outgoing INVITE request can be set, by setting RURI, From and To parameters. If any of those parameters is not set, the corresponding value of the incoming request is used. @@ -152,6 +182,10 @@ The patterns which can be used are the following: next_hop_port=$Hp(P-SomeNH-URI) + $M(value=>regexmap) - map a value (any pattern) to a regexmap (see below) + Example: $M($fU=>usermap) + + \\ -> \ \$ -> $ \* -> * @@ -168,6 +202,31 @@ If a space is contained, use quotation at the beginning and end. Example: To="\"someone\" <$aU@mytodomain.com>" +Regex mappings ($M(key=>map)) +----------------------------- + +A regex mapping is a (sorted) list of "regular expression" => "string value" pairs. +The regex mapping is executed with a key - any string, replacement pattern or +combination - and the first regular expression that matches returns the "string value". + +Regex mappings are read from a text file, where each line corresponds to one +regex=>value pair. The mappings to load on startup are set with the regex_maps +config option, the file name from where it is loaded is ".conf" in +the plugin config path. + +Mappings can also loaded into the running server by using the setRegexMap DI function +or the included sems-sbc-*-regex-* scripts: + + sems-sbc-set-regex-map load a regex map from a file + sems-sbc-get-regex-map-names list regex map names + + Example regex map: + ~~~~~~~ usermap.conf ~~~~~~ + # this is a comment + ^stefan=>stefansayer + ^frank=>frankmajer + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Setting Call-ID --------------- For debugging purposes, the call-id of the outgoing leg can be set to depend on