https://www.opensips.org/Documentation/Script-Tran-2-4
前言
直观地说,Transformation 指的是作用于一个变量的函数(脚本变量、伪变量、AVP、静态字符串),从输入中获取特定的值。但不改变原输入值。
下面是一个OpenSIPS 脚本实例,说明各种变量的用法:
# check if username in From header is equal with username in To header
if ($fU == $tU) {
...
}
# Request-URI username based processing
switch ($rU) {
case "1234":
...
break;
case "5678":
...
break;
default:
...
}
# assign an integer value to an variable
$var(gw_count) = 1;
# assign a string value to an AVP
$avp(server) = "opensips";
# store the Request-URI in a variable
$var(ru_backup) = $ru;
# concat "sip:" + From username + "@" + To domain in a script variable x
$var(x) = "sip:" + $fU + "@" + $td;
transformation旨在获取变量的各种属性值(比如值的长度、局部取值,取子串),或者计算其它转换值(比如说通过hexa编码、md5 、对DB操作的escape/unescape)。
语法上,transformation出现在一对花括号 '{' '}' 之间,并且紧跟在变量名之后。使用transformation时,变量名和transformation必须用用一对括符 '()'限定。
上实例:
# the length of From URI ($fu is pseudo-variable for From URI)
$(fu{s.len})
一个变量可以同时适配多个transformation。
# the length of escaped 'Test' header body
$(hdr(Test){s.escape.common}{s.len})
除非有特别的定义,否则,所有transformation在处理出错时返回NULL(比如说,通过"{uri.param,name}" transformation查找一个不存在的URI参数)。此外,NULL输入是可接受的,这样就可以支持上一条transformation返回NULL的链式操作。
任何地方都可以使用transformation,脚本变量支持,XLOG支持,AVP支持,甚至模块导出函数的参数中也支持,当然,表达式中的支持就更不在话下了。
重要提示:想进一步了解变量中是怎样使用transformation的话,请参考:https://www.opensips.org/Documentation/Script-CoreVar-2-2。
1. 字符串Transformation
这些transformation 的命名以字符's.'打头,它们的作用就是操作字符串变量。可用transformation 如下:
1.1 {s.len} 🔗
返回变量所存储字符串的长度。
$var(x) = "abc";
if($(var(x){s.len}) == 3)
{
...
}
1.2 {s.int} 🔗
把字符串转换为数字(整型),如果输入根本不包含数字信息,那么返回0。
$var(dur) = "2868.12 sec";
if ($(var(dur){s.int}) < 3600) {
...
}
1.3 {s.md5} 🔗
返回md5 Key
xlog("MD4 over From username: $(fU{s.md5})");
1.4 {s.substr,offset,length} 🔗
返回子串,子串以offset位置为起点,以参数length指定长度。如果offset为负数,那么从字符串的结尾处开始计数,-1为最后一个字符。offset为正数时, 0表示第一个字符。Length必须为正数,如果值为0,表示返回从offset到结束点的所有字符。offset和length可以是变量。
实例:
$var(x) = "abcd";
$(var(x){s.substr,1,0}) = "bcd"
1.5 {s.select,index,separator} 🔗
返回变量里的一个字段。字段以separator为分割符,以index为索引。separator 必须是一个字符,用作分割符。Index 必须为一个整数或变量。如果Index 值为负,那么表示从后往前索引,-1表示最后一个字段。如果Index 值为正数,索引0表示第一个字段。注意:如果某个字段为空,那么返回的是空值,不是NULL。
实例:
$var(x) = "12,34,56";
$(var(x){s.select,1,,}) => "34" ;
$var(x) = "12,34,56";
$(var(x){s.select,-2,,}) => "34"
1.6 {s.encode.hexa} 🔗
返回变量的hexa编码值。
1.7 {s.decode.hexa} 🔗
返回变量的hexa解码值。
1.8 {s.escape.common} 🔗
返回变量的转义串,被转义的字符有', ", 和0。在DB查询时非常有用,但如果使用的是非拉丁字符集,那么要注意。
1.9 {s.unescape.common} 🔗
上一条transformation的逆向操作。
1.10 {s.escape.user} 🔗
转义操作,把RFC规范中不允许出现在SIPURI里的字符,转换为 '%hexa'格式。
1.11 {s.unescape.user} 🔗
上一条transformation的逆向操作。
1.12 {s.escape.param} 🔗
转义操作,把RFC规范中不允许出现在SIPURI的参数中的字符,转换为 '%hexa'格式。
1.13 {s.unescape.param} 🔗
上一条transformation的逆向操作。
1.14 {s.tolower} 🔗
转换为小写的ASCII字符。
1.15 {s.toupper} 🔗
转换为大写的ASCII字符。
1.16 {s.index} 🔗
从第一个字符串的开始处查询另一个字符串。返回字符串起始处的索引,如果查询失败则返回NULL。有一个可选参数index,它指定开始查找位置的偏移量,支持负数。
$var(strtosearch) = 'onetwothreeone';
$var(str) = 'one';
# Search the string starting at 0 index
$(var(strtosearch){s.index, $var(str)}) # will return 0
$(var(strtosearch){s.index, $var(str), 0}) # Same as above
$(var(strtosearch){s.index, $var(str), 3}) # returns 11
# Negative offset
$(var(strtosearch){s.index, $var(str), -11}) # Same as above
# Negative wrapping offset
$(var(strtosearch){s.index, $var(str), -25}) # Same as above
#Test for existence of string in another
if ($(var(strtosearch){s.index, $var(str)}) != NULL)
xlog("found $var(sstr) in $var(strtosearch)");
1.17 {s.rindex} 🔗
从第一个字符串的结尾处查询另一个字符串。返回字符串起始处的索引,如果查询失败则返回NULL。有一个可选参数index,它指定开始查找位置的偏移量,查找到的字符串必须在指定的偏移量位置之前,支持负数。
$(var(strtosearch){s.rindex, $var(str)}) # will return 11
$(var(strtosearch){s.rindex, $var(str), -3}) # will return 11
$(var(strtosearch){s.rindex, $var(str), 11}) # will return 11
$(var(strtosearch){s.rindex, $var(str), -4}) # will return 0
1.18 {s.fill.left, tok, len} 🔗
在字符串的左侧填充字符或字符串,直到len指定的长度填充满为止。如果原字符串的长度大于或等于len指定的值,那么返回原字符串(不截断)。
$var(in) = "485"; (also works for integer PVs)
$(var(in){s.fill.left, 0, 3}) => 485
$(var(in){s.fill.left, 0, 6}) => 000485
$(var(in){s.fill.left, abc, 8}) => bcabc485
注意: 当前为了性能优化,不支持伪变量参数或小部件的"s.fill" 级联。
1.19 {s.fill.right, tok, len} 🔗
在字符串的右侧填充字符或字符串,直到len指定的长度填充满为止。如果原字符串的长度大于或等于len指定的值,那么返回原字符串(不截断)。如果输入变量是数值,会先转换为字符串
$var(in) = 485; (also works for string PVs)
$(var(in){s.fill.right, 0, 3}) => 485
$(var(in){s.fill.right, 0, 6}) => 485000
$(var(in){s.fill.right, abc, 8}) => 485abcab
1.20 {s.width, len} 🔗
指定长度为len,对输入进行截取或填充。填充时,以空字符为填料,填充在原字符串的右侧。同样,被截的内容也是右边的内容 。
$var(in) = "transformation";
$(var(in){s.width, 14}) => "transformation"
$(var(in){s.width, 16}) => "transformation "
$(var(in){s.width, 9}) => "transform"
1.21 {s.trim} 🔗
删除输入内容的前导的空白字符,还有尾随的空白字符。空白字符包括:空格、制表符、换行符,回车符。
$var(in) = "\t \n input string \r ";
$(var(in){s.trim}) => "input string"
1.22 {s.trimr} 🔗
从输入字符串中删除任何尾随的空白字符。空白字符包括:空格、制表符、换行符,回车符。
$var(in) = "\t \n input string \r ";
$(var(in){s.trimr}) => "\t \n input string"
1.23 {s.triml} 🔗
从输入字符串中删除任何前导的空白字符。空白字符包括:空格、制表符、换行符,回车符。
$var(in) = "\t \n input string \r ";
$(var(in){s.triml}) => "input string \r "
1.24 {s.dec2hex} 🔗
把一个十进制数转换为十六进制数,输出描述为字符串。
1.25 {s.hex2dec} 🔗
把一个十六进制数转换为十进制数。
1.26 {s.b64encode} 🔗
把输入的二进制数据转换为ASCII字符串格式。
$var(in) = "\x2\x3\x4\x5!@#%^&*";
$(var(in){s.b64encode}) => "AgMEBSFAIyVeJio="
1.27 {s.b64decode} 🔗
假设输入是Base64字符串,尽可能地对它进行解码。
$var(in) = "AgMEBSFAIyVeJio=";
$(var(in){s.b64decode}) => "\x2\x3\x4\x5!@#%^&*"
1.28 {s.xor,secret} 🔗
根据两个字符串的长度,通过"secret"字符串对输出字符串执行一次或多次XOR逻辑运算。
$var(in) = "aaaaaabbbbbb";
$(var(in){s.xor,x}) => "!/>^P!/>^P!^U2^Q!^U2^Q"
2. URI Transformation
名字以 'uri.'打头的transformation。它们返回SIP URI的组件。如果指定的组件缺失,那么返回空字符串。
2.1 {uri.user} 🔗
返回URI里的USER组件。
2.2 {uri.host} 🔗
(和 {uri.domain}作用相同)
返回URI的domain组件。
2.3 {uri.passwd} 🔗
返回URI的password组件。
2.4 {uri.port} 🔗
返回URI的port
2.5 {uri.params} 🔗
以字符串形式返回所有的URI参数。
2.6 {uri.param,name} 🔗
返回name指定的URI参数。
2.7 {uri.headers} 🔗
返回URI header
2.8 {uri.transport} 🔗
返回URI的transport 参数
2.9 {uri.ttl} 🔗
2.10 {uri.uparam} 🔗
返回URI的user参数
2.11 {uri.maddr} 🔗
返回URI的maddr 参数.
2.12 {uri.method} 🔗
返回URI的method 参数.
2.13 {uri.lr} 🔗
返回URI的lr参数.
2.14 {uri.r2} 🔗
返回URI的r2参数.
2.15 {uri.schema} 🔗
返回URI的schema。
3. VIA Transformation
名字以'via.'打头的transformation。输入变量为SIP的Via头域。这些transformation返回via头域的组件(参考via_body结构体的定义)。如果请求的组件缺失,那么返回空字符串。如果输入的Via头域值为空,那么将导致执行失败。以下描述如果没有特别说明,那么transform的返回值都是字符串(不是整数)。
实例:
$var(upstreamtransport) = $(hdr(Via)[1]{via.transport}{s.tolower});
$var(upstreamip) = $(hdr(Via)[1]{via.param,received});
$var(clientport) = $(hdr(Via)[-1]{via.param,rport});
3.1 {via.name} 🔗
返回 protocol-name
( RFC3261 BNF的描述),通常为SIP。
3.2 {via.version} 🔗
返回 protocol-version
( RFC3261 BNF的描述),通常是2.0
.
3.3 {via.transport} 🔗
返回transport
( RFC3261 BNF的描述)取值通常为: UDP
, TCP
, TLS。它描述的是消息的传输方式。
3.4 {via.host} 🔗
(和{via.domain}等效
)
返回 sent-by
( RFC3261 BNF的描述)里的host。通常是请求发送方的IP地址,它标识对方在哪里等待接收应答消息。
3.5 {via.port} 🔗
返回 sent-by
( RFC3261 BNF的描述)里的port。通常是请求发送方的端口,它标识对方在哪里等待接收应答消息。这个transform返回的结果既是数字,也是字符串。
3.6 {via.comment} 🔗
与via头域关联的comment。via_body
结构体中有这个字段定义,但是RFC3261中并没有明确允许Via携带comments(第221页中有提及,但是BNF 里并没有说明)。comment是一段用括号括起的文本。
3.7 {via.params} 🔗
以字符串返回Via头域的所有参数( RFC3261 BNF里的via-param
)。返回结果可以用 {param.*}这个
transform。这几乎提取了除了host和port之外的所有内容。
3.8 {via.param,name} 🔗
返回name指定的Via参数。典型的参数包括 branch
, rport
和received。
3.9 {via.branch} 🔗
返回VIA头域中的branch参数
3.10 {via.received} 🔗
返回VIA头域中的received参数
3.11 {via.rport} 🔗
返回VIA头域中的rport 参数。
4. 参数表 Transformation
名字以"param."打头的transformation。输入的变量值的格式是一个格式化的字符串,格式:name1=value1;name2=value2;...这些transformation返回特定参数的值,或者指定索引返回参数名。
4.1 {param.value,name} 🔗
返回名为'name'的参数的值
"a=1;b=2;c=3"{param.value,c} = "3"
4.2 {param.exist,name} 🔗
如果名为name
的参数存在,那么返回1,否则返回0,与参数是否有值无关。同时返回字符串和数字。name
可以是一个变量。它可用于检测没有值的参数的存在。
"a=0;b=2;ob;c=3"{param.exist,ob}; # returns 1
"a=0;b=2;ob;c=3"{param.exist,a}; # returns 1
"a=0;b=2;ob;c=3"{param.exist,foo}; # returns 0
4.3 {param.valueat,index} 🔗
返回index位置的参数值(index从0开始)。
"a=1;b=2;c=3"{param.valueat,1} = "2"
'index' 可以是另一个参数
4.4 {param.name,index} 🔗
返回index位置的参数名
"a=1;b=2;c=3"{param.name,1} = "b"
4.5 {param.count} 🔗
返回参数的个数
"a=1;b=2;c=3"{param.count} = 3
5. Name-address Transformation
名字以'nameaddr.'打头的transformation。处理变量类型为'[display_name] uri'这样的。transformation返回特定的字段值。
5.1 {nameaddr.name} 🔗
返回display name
'"test" <sip:test@opensips.org>' {nameaddr.name} = "test"
5.2 {nameaddr.uri} 🔗
返回URI
'"test" <sip:test@opensips.org>' {nameaddr.uri} = sip:test@opensips.org
5.3 {nameaddr.len} 🔗
返回整个name-addr组件值的长度
5.4 {nameaddr.param,param_name} 🔗
返回参数表中,名为param_name的参数的值。
'"test" <sip:test@opensips.org>;tag=dat43h' {nameaddr.param,tag} = dat43h
5.5 {nameaddr.params} 🔗
返回完整的参数表,包括参数名和值。
'"test" <sip:test@opensips.org>;tag=dat43h;private=yes' {nameaddr.params} = "tag=dat43h;private=yes"
6. IP Transformation
名字以'ip.'打头的transformation。
6.1 {ip.pton} 🔗
返回二进制描述的IP
"192.468.2.434" {ip.pton} returns a 4 byte binary representation of the IP provided
6.2 {ip.ntop} 🔗
把二进制IP转换为点分十进制的字符串。
"192.468.2.434"{ip.pton}{ip.ntop} = "192.468.2.434"
6.3 {ip.isip} 🔗
判断输入是否为有效的 IPv4 或IPv6地址,是返回1,否则返回0。
"192.468.2.434" {ip.isip} = 1
"192.468.2.434.1" {ip.isip} = 0
6.4 {ip.family} 🔗
如果二进制IP是 IPv4 或IPv6,那么返回 INET 或INET6。
"192.468.2.434" {ip.pton}{ip.family} = "INET"
6.5 {ip.resolve} 🔗
返回域名解析得出的IP地址。如果输入是个IP,那么Transformation不生效。
"opensips.org" {ip.resolve} = "78.46.64.50"
7. CSV Transformation
名字以"csv."打头的transformation。变量值为CSV格式:"field1,field2,..."。它返回字段的计数,或指定位置的字段值。
7.1 {csv.count} 🔗
返回指定CSV串里的实体数量:
"a,b,c" {csv.count} = 3
7.2 {csv.value} 🔗
返回指定位置的字段值。索引以0开始:
"a,b,c" {csv.value,2} = c
8. SDP Transformation
名字以"sdp."打头的transformation。变量的值为合法的SDP消息。它返回SDP消息里的指定行。
细分如下:
8.1 {sdp.line} 🔗
返回SDP的指定行。这个transformation还可以接收第二个参数,它指定第一个参数从SDP里取出多个值时的行号。索引以0开始。如果缺少第二个参数,那么取缺省值0。实例:
if (is_method("INVITE"))
{
$var(aline) = $(rb{sdp.line,a,1});
xlog("The second a line in the SDP body is $var(aline)\n");
}
if (is_method("INVITE"))
{
$var(mline) = $(rb{sdp.line,m});
xlog("The first m line in the SDP body is $var(mline)\n");
}
9.正则表达式Transformation
名字以"re."打头的transformation。输入可以是任意字符串。
9.1 {re.subst,reg_exp} 🔗
reg_exp参数可以是纯文本,也可以是变量。reg_exp的格式如下:
/posix_match_expression/replacement_expression/flags
其中flag的取值及含义如下:
i - 匹配时忽略大小写
s - 多行匹配
g - 替换所有匹配的值
实例:
$var(reg_input)="abc";
$var(reg) = "/a/A/g";
xlog("Applying reg exp $var(reg) to $var(reg_input) : $(var(reg_input){re.subst,$var(reg)})\n");
...
...
xlog("Applying reg /b/B/g to $var(reg_input) : $(var(reg_input){re.subst,/b/B/g})\n");
10. 实例
对一个变量,可以同时执行多个transformation,执行顺序从左到右。
- 取参数中位置索引1的值的长度(注意位置索引,0表示第一个,1是第二个)。
$var(x) = "a=1;b=22;c=333";
$(var(x){param.value,$(var(x){param.name,1})}{s.len}) = 2
- 判断是不是un-registration 消息
if(is_method("REGISTER") && is_present_hf("Expires") && $(hdr(Expires){s.int})==0)
xlog("This is an un-registration");