📄 rfc3028.txt
字号:
header :contains "from" "fool@example.edu") {
discard;
}
2.6. 参数
大多数命令都需要参数指明要执行的操作,参数分为三类:位置参数,标志参数和可选
参数。
2.6.1. 位置参数
位置参数的含义依赖于给出的顺序,因此使用位置参数必须按照的规定的顺序给出。
2.6.2. 标志参数
本文档规定的标志参数以类似于Common LISP的风格给出,大多数命令行系统也采用
相似的方式给命令传递标志。标志参数以冒号“:”开始,后面跟着参数的标识名,比如
“: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" <comparator-name: string>
因此在下面的例子中,所有主题类似于“You can MAKEMONEY FAST”的消息都被丢
弃,但是由于采用的比较运算子对大小写敏感,以“You can Make Money Fast”为主题的消
息就会保留下来。
if header :contains :comparator "i;octet" "Subject"
"MAKE MONEY FAST" {
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. 命令
滤网描述程序由命令组成。命令可以使用上述任何符号作为参数,参数既可以是位置参
数,也可以是标志参数,有些命令没有参数。命令分为三类:测试命令、动作命令和控制命
令。最简单的是动作命令,动作命令是一个标识符,后面可以没有参数也可以有多个参数,
动作命令以分号结束,不能以测试或者块作为参数。控制命令与此类似,但是测试命令的参
数是一个测试,而且只能以块而不是分号结束。测试命令作为控制命令的一部分,用于确定
是否要执行后面的块。
2.10. 求值
2.10.1. 动作之间的影响
某些动作不能与其它的动作一起使用,否则结果很荒谬,本文后面还会提到。扩展的动
作命令必须说明与本文规定的动作之间的相互作用。
2.10.2. 隐含留存
以前的经验表明过滤系统最好避免对大小写的区分,滤网语言采用“隐含留存”的方法
防止误删邮件。隐含留存就是说只要某个动作没有禁止则自动把邮件保存下来。只要没有把
邮件写入信箱、转存到其他地址或者明确地删除就执行隐含留存操作。某些动作可能需要保
留隐含留存的操作,这些动作不会直接影响邮件的传递,但可能有这方面的副作用。本规范
所定义的动作没有涉及这个问题,但是以后扩展中肯能要遇到。比方说,对于上面提到的短
消息,下面的描述语句不会产生任何影响,就是说隐含留存在发挥作用。
if size :over 500K { discard; }
2.10.3. 邮箱中的消息的唯一性
实现不应把同一个消息重复投送到一个信箱,既是描述程序明确要求把一个消息两次写
入同一个信箱。至于如何检查两个邮件是否相同取决于具体实现的定义。如果描述程序要求
再次把某个邮件写入同一个信箱不能认为是错误。
2.10.4. 动作数量的限制
基于站点的管理策略,可能要对所执行操作的数量或者同时执行的操作进行限制。如果
描述程序违反了站点对特定邮件的处理次数限制就要发生错误。实现必须避免再次发生同样
的错误。实现至少要允许一次留存或者转存(fileinto)操作,如果不能转存,至少要能够留
存。其它的动作要避免被拒绝。
2.10.5. 扩展特性与可选特性
考虑到不同邮件系统的不同性能,本规范定义了几种可选特性。在执行这类扩展操作之
前,必须使用“require”声明。如果某种扩展不能够“require”,则必须被视作完全不支持
该操作。如果描述程序无法识别“require”声明的某种扩展操作,则不执行该描述程序。
注意:这样规定是因为像LISP和TCL之类语言的经验表明,提示一下某个描述程序使
用了扩展成分是一种有效的方法。PostScript的实践经验则建议,最好不要采用这样的机制
——让描述程序的工作依赖于不存在的扩展。
定义新的动作命令的扩展必须说明与本文所定义的动作之间的交互作用。
2.10.6. 错误
任何程序语言都会发生变异错误和运行错误。编译错误是指进行语法检查时可以检查出
的错误,运行错误是只有在程序运行时才能发现的错误,其中包括磁盘写满之类的临时错误,
也包括非法的操作组合之类的问题。滤网描述语言在发生错误时体制所有的操作。在实现时
可以采用完整解析的策略,先测试所有的描述语句然后再执行所有的操作,甚至可以采用原
子执行 策略(要末全部执行要一个也不执行)。或者采用边解释边执行的策略,这样实现起
来简单,但是存在部分操作出错的问题(某些动作出错,其他的则正常执行)。实现也可以
在执行之前先排除不合法的操作集合(比如,reject + fileinto),但是这样可能会带来所谓的
“当机问题”。上述各种方法都是允许的,当机问题不在本规范的讨论之列。如果发生了错
误,实现必须通知用户在执行哪个操作时发生了错误,并执行隐含留存操作。
2.10.7. 执行上的限制
具体的实现可能要多某些结构进行限制,不过本规范对此没有过多的要求:至少要支持
15级的块嵌套和15级的测试列表嵌套。
3. 控制命令
多重操作和条件操作必须是用控制命令。
3.1. 控制结构if
if控制结构包括三部分:“if”、“elsif”和“else”,从语法上讲每一部分都是单独的一个
命令,但是“elseif”只能跟在“if”之后使用,“else”只能用在“if”或“elseif”后面,否
则就是错误的。
语法: if <test1: test> <block1: block>
语法: elsif <test2: test> <block2: block>
语法: else <block>
这些控制命令的含义与在其它语言中的含义类似,解释器遇到“if”时首先计算相关的
测试,如果结果为真则执行接下来的命令块。否则计算后面的第一个“elseif”相关的测试
(如果有的话),如果结果为真,则执行其后的命令块。Elseif后面可能还有其它的elsif,
如果这样的话就反复进行同样的测试。如果所有的“elsif”都不成立而且出现了“else”,则
执行“else”后的命令块。这样在多个命令块组成的链中,只选择执行其中的一个。
下面的例子中,邮件A和B都被下载到信箱中:
Example: require "fileinto";
if header :contains "from" "coyote" {
discard;
} elsif header :contains ["subject"] ["$$$"] {
discard;
} else {
fileinto "INBOX";
}
如果执行下面的程序,则邮件A被转存到acm@example.edu,而邮件B则转存到
postmaster@example.edu中,其他的邮件都被转存到field@example.edu。
Example: if header :contains ["From"] ["coyote"] {
redirect "acm@example.edu";
} elsif header :contains "Subject" "$$$" {
redirect "postmaster@example.edu";
} else {
redirect "field@example.edu";
}
注意这里与C语言不同,“else”和“if”之间没有空格,这样做是为了避免引起混淆。
3.2. 控制结构Require
语法: require <capabilities: string-list>
该动作说明描述程序要使用的特定扩展,如前所述在需要规范的扩展部分时必须使用
require进行声明,如果需要多个扩展成分可以在一条require语句中声明。如果出现require
命令,它必须放在所有其他命令之前,否则会出现错误。
例1: require ["fileinto", "reject"];
例2: require "fileinto";
require "vacation";
3.3. 控制结构Stop
语法: stop
该动作结束所有的操作,如果没有任何操作执行,则执行保存。
4. 动作命令
本文定义了5个用于处理邮件的动作:keep、fileinto、redirect、reject和discard。本语
言的具体实现必须支持“keep”、“discard”和“redirect”三个动作,应该提供“reject”和“fileinto”
命令,还可以对特定操作的次数予以限制。
4.1. 拒收(reject)
语法: reject <reason: string>
“拒收”操作是可选的命令,拒绝接收邮件并反馈给发送方一个[MDN]。这个命令把消
息返回给发送方,并说明接收方拒绝收取。在下面的程序中,消息A被拒收并返回给接收
方。
例1: if header :contains "from" "coyote@desert.example.org" {
reject "I am not taking mail from you, and I don't want
your birdseed, either!";
}
退回邮件按照[MDN]规范必须采用MDN错误的格式,其中的可读部分(MDN的第一
个成分)说明错误的原因,同时应通知发送方该邮件被过滤程序拒绝了。比方说内容可以是:
------------------------------------------------------------
接收方的邮件过滤程序拒收该邮件,原因如下:
我不接受你的邮件,也不上你的当!
------------------------------------------------------------
按照MDN规范MDN操作值字段必须是“deleted”,可以采用MDN自动发送和自动操
作模式集。某些实现可能不支持这个可选命令,使用前必须使用require命令声明“reject”。
4.2. 转存(fileinto)
语法: fileinto <folder: string>
“转存”操作把消息保存到指定的文件夹,实现应该支持该命令,但在某些环境中也许
不能执行这样的操作。在使用之前应在require命令中声明“fileinto”。 例如下面的语句将
把消息A保存到“INBOX.harassment”文件夹中:
Example: require "fileinto";
if header :contains ["from"] "coyote" {
fileinto "INBOX.harassment";
}
4.3. 转发(redirect)
语法: redirect <address: string>
“转发”命令用于把消息发给指定地址的另外一个用户,它不改变消息体和消息头,但
可以增加一个新的消息头,这个命令修改了邮件的接收方。该命令执行MTA类型的“重发”
操作(UNIX采用的风格),SMTP信封上的地址被转发命令中的新地址代替并在后台发出。
(注意这里与MUA类型的重发不同,MUA要创建一个发送方和消息ID都不同的新邮件,
在新邮件内装上原来的邮件。)只要一条简单的语句就可以转发所有的邮件,例如:
redirect "bart@example.edu";
实现应该设法通过循环控制为消息加上消息头或者计数收到的消息头,如果实现检测到
某个循环,就会导致错误。
4.4. 保存(keep)
语法: keep
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -