Kamailio Transformations - 常用的伪变量转换函数
伪变量转换函数大部分在 pv 模块中处理,官方文档记录的很全面,但是给的例子比较少。本文列举比较常用的转换函数。
参数列表转换
{param.value,name[, delimiter]}
根据名字从参数列表里面找到对应的项目,并返回其值 name 是参数名字,delimiter 是定界符。
$var(x) = "a=1;b=2;c=3";
$var(c) = $(var(x){param.value,c}); // "3"
// 默认定界符是分号,但也可以指定其它定界符(逗号除外)
$var(x) = "a=1&b=2&c=3";
$var(c) = $(var(x){param.value,c,&}); // "3"
// 下面的代码是错误的,定界符不能用逗号
$var(x) = "a=1,b=2,c=3";
$var(c) = $(var(x){param.value,c,,}); // 错误提示是 invalid separator in transformation: value,c,,
这个函数非常有用,比如我们可以在 FreeSWITCH 里面配置一个网关,realm 指向 Kamailio ,contact-params 指向 IMS。
<param name="realm" value="192.168.1.100"/> <!--Kamailio-->
<param name="contact-params" value="gwaddr=192.168.1.200:5060"/> <!--IMS-->
Kamailio 的 request 路由可以这样写:
route[SRC_FREESWITCH] {
$var(params) = $(sel(contact.uri.params)); // 取 contact 的参数
$var(gwaddr) = $(var(params){param.value,gwaddr}); // 取网关地址
$du = "sip:" + $var(gwaddr);
t_relay();
exit;
}
{param.valueat,index[, delimiter]}
根据索引从参数列表里面找到对应的项目,并返回其值,index 是索引号(从 0 开始),delimiter 是定界符。
$var(x) = "a=1;b=2;c=3";
$var(b) = $(var(x){param.valueat,1}); // "2"
字符串转换
{s.int}
字符串转整数
$var(x) = "1234";
$var(i) = $(var(x){s.int}); // 1234
$var(x) = "1234.2";
$var(i) = $(var(x){s.int}); // 出错
$var(x) = "s1234";
$var(i) = $(var(x){s.int}); // 出错
{s.numeric}
删除字符串的所有非数字部分,并转成整数。
$var(x) = "(040)1234/567-89";
$var(num) = $(var(x){s.numeric}); // 040123456789
{s.ftime,format}
根据参数格式化 pv 变量中的 epoch 时间,该参数必须是 strftime 格式化的字符串形式。
$var(local_time) = $(TS{s.ftime,%Y-%m-%d %H:%M:%S}); // 例子:"2023-04-11 15:41:59"
{s.unquote}
删除字符串里面的单引号对和双引号对。
$var(x) = "'alice'";
$var(alice) = $(var(x){s.unquote}); // "alice"
$var(x) = "'alice";
$var(alice) = $(var(x){s.unquote}); // "'alice",没有变化,因为单引号没有成对
{s.unbracket}
删除字符串里面的 ()
对、 []
对、 {}
对以及 <>
对。
$var(x) = "<sip:alice@test.sip>";
$var(uri) = $(var(x){s.unbracket}); // "sip:alice@test.sip"
用这个函数来处理 Contact 头比较方便
URI 转换
$var(contact) = "sip:1001@192.168.1.100:5080;transport=udp;a=1;b=2";
$var(a)=$(var(contact){uri,param,a}); // "1"
$var(c)=$(var(contact){uri,param,c}); // ""
$var(a)=$(var(contact){uri.params}); // "transport=udp;a=1;b=2"
$var(a)=$(var(contact){uri.host}); // "192.168.1.100"
$var(a)=$(var(contact){uri.port}); // "5080"
$var(a)=$(var(contact){uri.user}); // "1001"
$var(a)=$(var(contact){uri.scheme}); // "sip"
多行文本转换
{line.count}
返回多行文本的行数。
{line.sw,match}
返回以 match 开头的行。
# 字符串赋值
$var(sdp) = "v=0\r\n";
$var(sdp) = $var(sdp) + "o=freeswitch 1680834355 1680834356 IN IP4 192.168.1.100\r\n";
$var(sdp) = $var(sdp) + "s=freeswitch\r\n";
$var(sdp) = $var(sdp) + "c=IN IP4 192.168.1.100\r\n";
$var(sdp) = $var(sdp) + "t=0 0\r\n";
$var(sdp) = $var(sdp) + "m=audio 2560 RTP/AVP 8 101\r\n";
$var(sdp) = $var(sdp) + "a=rtpmap:8 PCMA/8000\r\n";
$var(sdp) = $var(sdp) + "a=rtpmap:101 telephone-event/8000\r\n";
$var(sdp) = $var(sdp) + "a=fmtp:101 0-15\r\n";
$var(sdp) = $var(sdp) + "a=sendrecv\r\n";
$var(sdp) = $var(sdp) + "a=ptime:20\r\n";
$var(media) = $(var(sdp){line.sw,m=audio}); // 得到的值是 "m=audio 2560 RTP/AVP 8 101",$var(sdp) 是多行文本,line.sw 函数找到包含 m=audio 的那一行
$var(count) = $(var(sdp){line.count}); // 得到的值是 11 ,`$var(sdp)` 有 11 行
这个函数有用,比如下面的路由代码把 sdp 里面的 ptime 属性强制修改成 10 (尽管很少这么做)。
loadmodule "textops.so"
loadmodule "textopsx.so"
loadmodule "sdpops.so"
...
route[MODIFY_PTIME_10] {
if (has_body("application/sdp")) {
$var(body) = $sdp(body);
$var(ptime_line) = $(var(body){line.sw,a=ptime});
$var(new_ptime_line) = "a=ptime:10";
replace_body($var(ptime_line), $var(new_ptime_line));
msg_apply_changes();
xinfo("newsdp = $sdp(body)\n");
}
}
正则表达式替换
{re.subst,expression}
此转换类由 textops 模块导出,对伪变量执行 POSIX 正则表达式替换。
$var(s) = "9123456789";
$var(s1) = $(var(s){re.subst,/^9(.*)/\1/}); // 去掉字冠 9,得到 123456789
再举一个比较实用的例子:
$var(contact) = '"XiaoYingTao" <sip:6753997@xwitch.cn:5060>';
$var(uri) = $(var(contact){re.subst,/^.*<(sip:.*)>/\1/}); // 得到 sip:6753997@xwitch.cn:5060
还有一个例子:
$var(sdp) = "v=0\r\n";
$var(sdp) = $var(sdp) + "o=- 1704250570 3 IN IP4 192.168.1.100\r\n";
$var(sdp) = $var(sdp) + "s=-\r\n";
$var(sdp) = $var(sdp) + "c=IN IP4 192.168.1.100\r\n";
$var(sdp) = $var(sdp) + "t=0 0\r\n";
$var(sdp) = $var(sdp) + "m=audio 30608 RTP/AVPF 8\r\n";
$var(new_sdp) = $(var(sdp){re.subst,/AVPF/AVP/g}); # 把 sdp 里面的 AVPF 替换成 AVP
json 转换
此转换类由 json 模块导出。
loadmodule "json.so"
...
$var(j) = '{"a":"1", "b":2}';
$var(a) = $(var(j){json.parse,a}); // "1"
$var(b) = $(var(j){json.parse,b}); // "2" ,字符串,不是整数
$var(c) = $(var(j){json.parse,c}); // ""