string>”表示一个字符串类型的参数key。
与[ABNF]类似,文字串用引号表示,转义字符使用斜杠标志,允许使用括号进行组合。
在语法描述中,经常会遇到一些语法片段,如MATCH-TYPE、COMPARATOR和ADDRESS-PART,详细含义参见后述。
第10节所描述的形式命令语法仅仅作为构造命令的权威参考,但是形式语法没有规定命令形式参数的顺序、语义、个数和类型,也没有规定哪些命令名是合法的,形式语法的意义在于以后对该语言进行扩展时可以保证语法的一致性。
1.2.邮件消息样例
下面是本文所使用的邮件样例。
MessageA
-----------------------------------------------------------
Date:
Tue,1Apr199709:
06:
31-0800(PST)
From:
coyote@desert.example.org
To:
roadrunner@
Subject:
Ihaveapresentforyou
Look,I'msorryaboutthewholeanvilthing,andIreally
didn'tmeantotryanddropitonyoufromthetopofthe
cliff.Iwanttotrytomakeituptoyou.I'vegotsome
greatbirdseedoverhereatmyplace--topoftheline
stuff--andifyoucomeby,I'llhaveitallwrappedup
foryou.I'mreallysorryforalltheproblemsI'vecaused
foryouovertheyears,butIknowwecanworkthisout.
--
WileE.Coyote"SuperGenius"coyote@desert.example.org
-----------------------------------------------------------
MessageB
-----------------------------------------------------------
From:
youcouldberich!
@reply-by-postal-mail.invalid
Sender:
b1ff@
To:
rube@landru.example.edu
Date:
Mon,31Mar199718:
26:
10-0800
Subject:
$$$YOU,TOO,CANBEAMILLIONAIRE!
$$$
YOUMAYHAVEALREADYWONTENMILLIONDOLLARS,BUTIDOUBT
IT!
SOJUSTPOSTTHISTOSIXHUNDREDNEWSGROUPS!
ITWILL
GUARANTEETHATYOUGETATLEASTFIVERESPONSESWITHMONEY!
MONEY!
MONEY!
COLDHARDCASH!
YOUWILLRECEIVEOVER
$20,000INLESSTHANTWOMONTHS!
ANDIT'SLEGAL!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
111111111!
!
!
!
!
!
!
11111111111!
!
1JUST
SEND$5INSMALL,UNMARKEDBILLSTOTHEADDRESSESBELOW!
-----------------------------------------------------------
2.设计
2.1.语言的形式
该语言由一系列命令组成,命令由多个空白字符分隔的记号组成。
命令标识符是第一个记号,后面可以有多个参数记号(也可以没有),参数可以是文字数据、标签、命令块或者测试命令。
该语言用UTF-8表示,ASCII记号区分大小写。
2.2.空白字符
空白字符用于分隔记号,包括制表符、新行字符(CRLF,不能只使用CR或LF)和空格,不考虑空白字符的个数。
2.3.注释
该语言提供了两种类型的注释,注释在语义上与空白字符是等效的,空白字符可以出现的地方都可以使用注释(只有一个例外,即多行字符串,参见后述)。
第一种格式是使用一个井号字符“#”(必须在表示字符串的引号外),后面的字符串直到下一个回车换行符都被视作注释内容。
例如:
ifsize:
over100K{#thisisacommentdiscard;}。
第二种方法是使用记号“/*”和“*/”(不能在字符串引号内),这样的注释可以是多行的,但不能嵌套,如:
ifsize:
over100K{/*thisisacomment
thisisstillacomment*/discard/*thisisacomment
*/;
}
2.4.文字数据
文字是指作为命令参数的非执行数据,仅仅就其本身取值,仅限于数字和字符串。
2.4.1.数字
数字是普通的十进制数,不过那些很大的数值比如消息的长度后面可以加上表示2的幂的字符“K”、“M”或者“G”。
它们的含义按照通常使用的国际单位制,K表示千或1024,M表示百万或1048576,G表示十亿或1073741824(2的30次幂)。
具体的实现至少要提供31bit的量级,本规范只使用正整数。
2.4.2.字符串
该描述语言在很多地方要使用字符串,比如模式匹配、地址以及文本体等等。
一般来说,大多数情况下使用引号括起来的短字符串就足够了,但是还提供了一种适用于长字符串如消息体的字符串格式。
引号字符串开始和结束都是双引号字符(ASCII34)引号字符串中可以包含反斜杠字符,但它后面必须是反斜杠或者双引号,前者表示反斜杠字符本身,后者表示字符串中的引号。
未经定义的转义序列作为不含反斜杠的序列处理(比如“\a”就认为是“a”)。
引号字符串中不能包含制表符、回车换行符和控制字符之类的非打印字符。
引号字符串允许跨行,但不能包含空字符NUL(ASCII0)。
为了便于处理大量的文本,该语言还提供了一种多行字符串。
多行字符串一“text:
”和一个回车换行符开始,最后以回车换行、句点和回车换行符结束。
为了避免与只包含一个句点的信息行混淆,我们采用填充法,即对于那些第一个字符是“.”的行在前面再人为增加一个额外的“.”。
服务器对这样的描述语句解释时再把多余的句点去掉。
注意,对于以句点开头但后面跟着的字符不是“.”的行,不作为句点填充处理,就是说“.foo”仍然是“.foo”。
由于这种情况有可能造成混乱,因此编辑器必须正确的处理句点填充避免出现这种情况。
注意,在“text:
”和CRLF之间可以加入斜杠开始的注释成分,但不能插入到字符串本身,也不允许使用“#”开始的注释。
2.4.2.1.字符串列表
由于模式匹配经常对一组字符串进行匹配,因此许多测试命令允许使用字符串列表,只要测试对其中的一个字符串成立测试结果即为真。
建议实现时对这种情况采用短循环求值的方法。
比方说,对于测试命令“header:
contains["To","Cc"]["me@","me00@landru.example.edu"]”,只要输入的消息包含邮件地址me@或者me00@landru.example.edu中的一个,测试结果即为真。
相反,只要允许使用字符串列表的地方,都可以使用单个的字符串,它被认为是只有一个成员的字符串列表。
就是说,测试命令“exists“to””和“exists[“to”]”是等价的。
2.4.2.2.信息头
信息头是一个字符串子集,根据Internet消息规范[IMAIL,RFC1123],每行信息头差不多任何地方都可以使用空白字符,在消息头的名称和后面的冒号之前也允许出现空白字符,但是消息头名称和后面冒号之间的空白字符在处理时被忽略掉。
消息头名称不能包含冒号,比如“From”表示以“From:
”或“From:
”开始的一行消息头,但是没有任何消息头与字符串“From:
”匹配。
对数据进行解释之前,跨行的消息头要合并成一行,各行位的CRLF以及下一行的前导空格转换成单个空格。
2.4.2.3.地址
有几个处理邮件地址的命令,邮件地址也是一种特殊的字符串。
如果地址字符串需要用于其它的应用环境,除了必须符合[IMAIL]的规定外,还有其他的限制。
按照[IMAIL]第6.1节定义的符号,地址的语法格式为:
sieve-address=addr-spec;simpleaddress
/phrase"<"addr-spec">";name&addr-spec
就是说,地址中不能出现路由或者地址组语法,如果需要使用多个地址,必须是用字符串列表,而不能用命名的地址组。
实现必须保证地址的语法是有效的,但不要求必须是邮件接收方。
2.4.2.4.MIME部分
在少数几个地方,[MIME]消息体部分也用字符串表示,其中包括MIME头部和消息体。
这样就提供了一种在滤网描述语言程序中嵌入其它类型数据的方法,从而能够在输出的消息中使用UTF-8之外的字符集。
2.5.测试
测试作为命令的参数用于控制命令的行为方式。
在本文中,测试以if/elseif/else的方式给出,根据测试结果选择要执行的代码段。
测试不能有副作用,就是说不能改变过滤器和消息的状态。
本规范规定的测试都没有副作用,同样的限制也适用于扩展测试命令。
这样做是因为带有副作用的测试难以阅读和维护,而且不方便通过图形用户界面生成描述语句。
只有那些作用明确的动作才允许改变过滤器的状态。
2.5.1.测试列表
有些测试可能需要多个测试参数(比如分别对应逻辑与和逻辑或的“allof”和“anyof”),测试列表元素提供了成组测试的方法。
例如:
ifanyof(notexists["From","Date"],
header:
contains"from""fool@example.edu"){
discard;
}
2.6.参数
大多数命令都需要参数指明要执行的操作,参数分为三类:
位置参数,标志参数和可选参数。
2.6.1.位置参数
位置参数的含义依赖于给出的顺序,因此使用位置参数必须按照的规定的顺序给出。
2.6.2.标志参数
本文档规定的标志参数以类似于CommonLISP的风格给出,大多数命令行系统也采用相似的方式给命令传递标志。
标志参数以冒号“:
”开始,后面跟着参数的标识名,比如“:
contains”。
标志参数的意思是后面的各个记号(可能没有)根据前面的标识名可能有特殊的含义。
标志参数后面的记号可以是数字或者字符串,但不能是块。
标志参数与位置参数很相似,只不过其含义取决于标志而不是命令本身。
标志参数必须出现在位置参数之前,但是不同的标志参数的顺序可以是任意的。
为了简化规范,在命令的语法定义中并没有作此规定,但是只要保证出现在位置参数之前,标志参数的位置可以互相交换。
标志参数和可选参数可以混合使用。
为了简化,标志参数不能再使用标志参数作为参数。
2.6.3.可选参数
可选参数与标志参数非常相似,只不过调用命令时可以不提供可选参数而采用某个缺省值。
由于可选参数能够有效地简化描述程序,因此比标志参数用的更广泛。
特别一提的是“:
comparator”参数,这个参数用于指定字符串比较所使用的[ACAP]比较运算子,因为UTF-8定义的某些语言可能采用的不同的字符序。
2.6.4.参数类型
简单地讲,参数可以是文字数据、测试或者命令块。
从这意义上说,“if”控制结构只不过是以测试和命令块作为参数而且能够执行那个代码块的一个命令。
但是这种笼统的提法从解析的观点来看可能会产生混乱,第9.2节对此给出了更明确的定义:
参数是字符串列表、数字和标志,后面可以带有测试或测试列表,还可以带有命令块。
但是测试或者测试列表以及命令块不能超过一个,以命令块结束的命令后面不能有分号。
2.7.字符串的比较
字符串的匹配方式有多种,可以分为三种类型:
完全匹配、子串匹配或通配符块方式的匹配,分别描述如下。
为了实现不同字符集和大小写字符之间的匹配,滤网语言借用了ACAP的比较运算子注册。
但是对于表示信息头名称的字符串之间的比较不能使用用户指定的运算子。
信息头的比较总是使用“i;ascii-casemap”运算子,就是说这种比较是大小写敏感的,因为IMAIL消息规范是如此规定的。
2.7.1.匹配类型
本规范使用了三种匹配类型:
“:
is”、“:
contains”和“:
matches”,可以作为标志参数传递给适当的命令。
“:
contains”类型描述的是子串匹配,只要后面的值参数中包含键参数,匹配的结果即为真。
比如,字符串“frobnitzm”包含“frob”和“nit”,但是不含“fbm”,空键值(“”)是任何串值的子串。
“:
is”类型描述的是完全匹配,只有第一个字符串的内容与第二个字符串的内容完全一致时才算匹配成功,字符串“frobnitzm”仅仅与其自身匹配,空键值也仅仅是空键值。
“:
matches”则使用字符“*”和“?
”规定通配符,“*”表示0或多个字符,“?
”表示单个字符。
要与这两个字符本身匹配时必须是用双斜杠进行转义,即分别使用“\\*”和“\\?
”。
前一个斜杠对第二个进行转义,两者合在一起对后面的“*”转义,虽然有点罗嗦,不过在一些使用团块和正则表达式的程序语言中也很常见。
为了明确采用何种类型的匹配,可以使用标志参数“:
match”、“:
is”和“:
contains”,如果没有指定匹配方式,则缺省采用“:
is”。
要注意,这些修饰成分与比较运算子相互作用,某些比较运算子无法与“:
matches”和“:
contains”一起工作,如果强制使用就会造成错误。
每个命令只能使用一个匹配参数,否则就是错误的。
为了方便说明,我们把匹配类型语法成分定义如下:
":
is"/":
contains"/":
matches"
2.7.2.跨字符集的比较
滤网描述语言用UTF-8表示,但是消息中可能使用多个字符集,因此实现时应该完成跨字符集的比较运算。
实现必须把消息头使用的字符集转换成UTF-8编码,只要两个字符串的UTF-8编码相等,就认为两个字符串时相同的。
另外,无论是消息头还是消息体,只要采用[MIME]规定的形式,都应能够对其解码,包括ISO-8859-*字符集UTF-8的ASCII子集、US-ASCII、ISO-8859-1。
如果实现上有困难,则至少要保证只要两个字符串有一个包含大于127的字节则两者不等。
2.7.3.比较运算子
匹配类型还可以带有一个比较运算子,以实现语言无关、大小写无关的串匹配。
比较运算子在[ACAP]中描述,ACAP定义注册项,本规范则引用那些注册项。
ACAP定义了许多比较类型,单本规范仅仅是用相等比较。
本语言的任何实现都必须支持“i;octet”运算子(简单的字节比较)和“i;ascii-casemap”运算子(把UTF-8的ASCII子集的大写字符和小写字符视作同一个字符)。
如果没有明确规定,缺省使用“i;ascii-casemap”。
某些比较运算子不适用于子串匹配,就是说只能用于“:
is”匹配。
如果试图对“:
contains”和“:
matches”使用不合适的运算子会造成错误。
比较运算子用可选参数“:
comparator”指定,语法如下:
":
comparator"string>
因此在下面的例子中,所有主题类似于“YoucanMAKEMONEYFAST”的消息都被丢弃,但是由于采用的比较运算子对大小写敏感,以“YoucanMakeMoneyFast”为主题的消息就会保留下来。
ifheader:
contains:
comparator"i;octet""Subject"
"MAKEMONEYFAST"{
discard;
}
其他的比较运算子属于扩展类型,必须声明之后才能使用,未知的运算子会导致操作失败。
“:
matches”和“:
contains”匹配类型都支持“i;octet”和“i;ascii-casemap”比较运算子。
运算子参数在一条命令中只能给出一次。
2.7.4.地址之间的比较
地址是最常使用的字符串。
这种字符串是结构化的,经常需要与某个地址的本地部分和域地址部分进行比较,因此专门处理地址的某些测试允许带有一个额外的可选参数。
这个可选参数可以是“:
localprat”、“:
domain”或“:
all”,分别对地址的本地部分、域地址部分和整个地址进行测试。
至于比较的方式,比如是否区分大小写由测试的运算子参数指定。
省略的情况下默认使用“:
all”。
地址参数的语法如下:
":
localpart"/":
domain"/":
all"
2.8.块
块是放在花括号之间的一组命令,引入块是为了使用控制结构。
控制结构是一种命令,其参数包括测试和块两部分,根据测试的结果决定块中的代码执行多少次。
本规范中提供的命令不包含循环结构,提供的控制结构(if、elseif和else)要么对块执行一次,要么一次也不执行。
因此只涉及到两个参数:
测试和块。
2.9.命令
滤网描述程序由命令组成。
命令可以使用上述任何符号作为参数,参数既可以是位置参数,也可以是标志参数,有些命令没有参数。
命令分为三类:
测试命令、动作命令和控制命令。
最简单的是动作命令,动作命令是一个标识符,后面可以没有参数也可以有多个参数,动作命令以分号结束,不能以测试或者块作为参数。
控制命