摘要

  • IMAP 把 \Seen 定义为“邮件已被阅读”,但服务器实际保存的是可修改的邮箱状态:BODY[...] 可隐式置位,BODY.PEEK[...] 可取回内容而不置位,有权限的客户端也能主动添加或清除它。
  • 若要声称“某个人读过”,必须把认证主体、客户端、邮箱世代、UID、命令、变更前后标记、服务器响应和界面显示事件连接起来。一个比特无法独自证明注意、理解、同意或回复。

一个很像人的动词,落在一个机器状态上

Mark Crispin 发明 IMAP,是为了让邮件保留在服务器上,同时能由不同地点、不同客户端访问。Stanford 的纪念记录写明,他在 1977 至 1988 年担任系统程序员期间发明了这项协议。IETF 的人物页列出他参与的 25 份 RFC,其中包括成为 IMAP4rev1 长期基准的 RFC 3501。Unicode Consortium 则以邮件专家和 IMAP 之父纪念这位长期贡献者。

RFC 3501 对 \Seen 的解释非常直接:“邮件已被阅读。”当前的 IMAP4rev2 标准 RFC 9051 保留了这层语义。这个词让多客户端邮箱拥有共同状态,价值无可置疑;问题在于,协议也明确区分人类“用户”与软件“客户端”。

服务器接收命令、返回数据、维护属性,却没有观察眼睛、注意力或理解力。因此最稳妥的陈述应当收窄为:在这个观察时点,邮箱保存着标准化的已读标记。若要把它提升为关于人的结论,就必须先知道这个比特是怎样出现的。

FETCH 会顺手写状态,PEEK 则不会

证据边界最清楚地写在 FETCH 语义里。客户端以 BODY[...] 请求正文区段时,RFC 3501 和 RFC 9051 要求隐式设置 \Seen;如果标记因此改变,服务器会报告新的 flags。一次取数同时成了一次状态写入。

BODY.PEEK[...] 是不会隐式设置 \Seen 的替代形式。于是同一协议允许几种不同事实:正文被取回且标记改变;正文被取回但标记不变;客户端没有取回正文,却用 STORE 写入已读;正文已经取过,后来又有人清除了标记。

这些选择并非缺陷。它们支持预览、离线同步和“稍后再读”。但也正因为如此,标记不是人的阅读传感器。索引、过滤、缓存或预览程序可能取回正文;这是协议允许的运行场景,不是对某个产品的事实指控。反过来,客户端也可能把 PEEK 得到的内容展示给人,而服务器仍保留未读状态。服务器的证据只到命令及其效果为止。

邮件进入邮箱时就可以是“已读”

APPEND 允许随新副本提交初始标记,两代 IMAP 规范都给出了带 \Seen 的示例。这时,比特描述的是入库状态,而不是证明有人在副本存入之后读过它。

STORE 还能主动添加或删除标记。“全部标为已读”可以不展示任何正文;“标为未读”也可在逐字阅读后清除比特。共享邮箱或多个客户端同时使用时,后来观察者看见的状态,可能由另一个获授权主体造成。

所以“未读”在逻辑上并不等于“从未有人读过”。它只说明当前没有 \Seen。历史中可能发生过正文抓取、手动回退、同步竞争或初始状态写入,而当前值已经把它们抹平。

权限决定服务器能否留下这道痕迹

RFC 4314 为改变 \Seen 单独定义了 s 权限。若当前用户没有这项权利,本来会隐式置位的 FETCH 不得写入该标记;用 STORE 修改时同样需要检查 s。

两名主体可以取得同一正文,却留下不同的持久记录:一人的会话使比特改变,另一人的权限禁止这次状态写入。没有 \Seen,可能证明的是访问控制边界,而不是没有展示过内容。

不同实现对共享与非共享标记也可有不同安排。因此完整问题不是“它已读吗”,而是“这是谁的状态、处于哪种邮箱模型、由哪项权限写入”。个人邮箱、客服共享箱和高管委托箱即使显示同一标记,也不构成同等的人类证据。

变更顺序不能代替变更原因

RFC 7162 的 CONDSTORE 与 QRESYNC 为元数据增加修改序列。客户端可以发现更新、修正缓存,并以 UNCHANGEDSINCE 为 STORE 设置前置条件。旧写入遇到新状态时可以显式失败,而不是悄悄覆盖。

这是一张顺序收据,不是一份人的证词。MODSEQ 能说明标记晚于某个版本发生变化,却不能独自分辨隐式 FETCH、显式 STORE、另一个客户端或外部代理,更不能说明屏幕显示了什么、是否有人注意。

RFC 8621 又把相近语义带到 JMAP,以 $seen 作为可由获授权用户增删的特殊关键词。跨协议同步可以让状态更加一致;它提高的是“各处都保存同一比特”的可信度,而不是这个比特对人类行为的观察范围。

用一串收据组成“已读”结论

可靠记录首先要保存认证主体、委托关系、客户端实例和会话;随后用邮箱的 UIDVALIDITY 与邮件 UID 锚定对象,避免把可复用的序号当成身份。再记录成因:正文 FETCH、PEEK、STORE、APPEND、同步或具名外部代理,以及请求区段、变更前后 flags、服务器响应、时间和可用的 MODSEQ。

界面真正显示正文,应是另一项事件;业务若需要人的确认,还应收集明确确认。于是记录可以诚实地写成:“客户端抓取正文;Seen 被隐式置位;是否显示未知;没有人工确认。”它没有“已读”两个字方便,却更能支持有后果的决定。

Crispin 的贡献,是让邮箱状态可以互操作。后来者的责任,是不让这项有用状态替它从未观察过的人作证。

来源