摘要

  • RFC 5232 允许 Sieve 为当前处理的邮件维护一组标记,并在 keep 或 fileinto 投递时附在该邮件副本上。
  • 这不是通用的 IMAP 邮箱编辑能力:目标邮箱无法永久保存的标记必须被忽略,而且不得因此把脚本执行判为失败。

标记跟着投递走

一封邮件会被解析、分流,最后进入某个邮箱。IMAP 标记属于邮件在存储中的状态。2008 年 1 月发布的 RFC 5232 把 Sieve 过滤语言接到了这类状态上,但没有抹掉“决定当前邮件如何投递”与“重写整个邮箱”之间的界线。

imap4flags 扩展要求实现四项操作或测试:setflag、addflag、removeflag、hasflag;它还为 keep 和 fileinto 增加了 :flags 参数。前三者维护标记集合,hasflag 检查集合中是否有指定名称。投递参数则把一组标记交给当前邮件的目标副本。

这组标记存在于一次 Sieve 执行之中。脚本开始时,内部标记变量为空;setflag 替换整组内容,addflag 增添成员,removeflag 移除成员,hasflag 可以检查当前集合。若解释器也支持独立的 Variables 扩展,脚本还可以使用命名变量;否则,显式写变量名会导致错误。标记名不区分大小写,脚本不能假定解释器会保留排列顺序、大小写或重复项。

真正的交接发生在投递动作处。keep 或 fileinto 带有 :flags 时,目标副本使用参数里给出的集合;省略它时,动作采用内部变量当前的值。这个默认值也适用于隐式 keep——也就是没有显式投递动作取代默认处理时的留存行为。脚本可以先在某个分支加入标记,再让后续投递动作沿用;也可以在投递时另行指定一组标记。

但 RFC 5232 只作用于当前这次 Sieve 执行正在处理的邮件。它不允许脚本从邮箱里挑出另一封旧邮件并修改其标记,也不影响其他动作作为副作用生成的另一封邮件。因此,这是一处投递钩子,不是任意改写 IMAP 存储的接口。

目标邮箱还构成第二道边界。:flags 表达希望副本带有哪些标记,却不能保证每个邮箱都能永久保存它们。解释器必须忽略无法永久保存的标记,并且不能仅因此让 Sieve 脚本在运行时失败。RFC 举例说明:若目标邮箱无法存储 \\Deleted,fileinto :flags "\\Deleted" 与不带这一请求的 fileinto 等效。在 IMAP 中,\\Deleted 表示等待之后 expunge 的删除标记,不等于已经物理擦除邮件。

所以,“规则设置了标记”这句话可能把多个环节压成一个:脚本分支是否成立、解释器形成了什么标记集合、投递动作提出了什么请求、邮箱永久保存了哪些标记,以及客户端最终向读者展示了什么。它们需要分别验证。RFC 5232 也没有规定解释器必须通过 IMAP 客户端访问服务器,还是直接操作邮件存储。实现架构可以不同,但其处理范围仍然是当前邮件。

如果多个投递动作最终合并为一份副本,RFC 5232 还规定了优先次序:执行重复消息消除时,最后给出的标记列表必须获胜。因此,最终副本不一定集合了每条分支曾经提出的标记。分析规则时要看动作顺序和实际投递机制,不能只把所有 addflag 加起来推断结果。

RFC 5232 与 Sieve 基础语言 RFC 5228、变量扩展 RFC 5229、关系比较扩展 RFC 5231 同属一个技术家族;这些文件说明规则如何解析与检查值,却没有改变标记随投递交接的边界。后来的 IMAP4rev2 RFC 9051 区分永久标记和仅限会话的标记,并将 \\Recent 标为已弃用。这提醒我们,请求一个标记不等于该标记必然持久存在。规范描述的是协议契约,而不是某个具体服务器已经实现、邮件已经送达或客户端界面已经显示标志的证据。

来源