摘要

  • RFC 2180 允许服务器在多客户端同时访问邮箱时拒绝删除、保留“幽灵”副本、断开其他会话,或让旧会话继续执行不依赖邮箱名的操作。
  • tagged OK 只证明一条命令按某种允许的策略完成;它不证明命名空间、消息序列、访问权和物理数据已经在所有会话中同时收敛。

名字消失了,内容还在被使用

设想客户端一和客户端二都已选择邮箱 FOO。客户端一执行 DELETE FOO,服务器回复 OK。随后客户端三执行 STATUS FOO,得到“邮箱不存在”。但客户端二仍可对已选择的邮箱执行 FETCH 或 STORE。

这不是 RFC 2180 忽略的漏洞,而是它列出的合规策略之一。服务器可以先从命名空间移除 FOO,让新会话再也找不到它,同时把内容留给已有引用;等最后一个已选择会话关闭后,才永久删除数据。服务器也可以在邮箱仍被使用时拒绝删除,或完成删除后向其他会话发送 BYE,迫使它们断开。

三种选择保护的是不同目标。拒绝删除维护活动会话,却可能让繁忙的共享邮箱几乎无法删除。保留幽灵副本维护连续性,却延迟真正的撤销,也阻止同名邮箱立即重建。强制断开让删除更决绝,却牺牲在线工作。RFC 没有把这些代价藏在一个“成功”词里。

重命名也能制造分层现实。服务器可以只改变邮箱的名称属性。旧会话继续执行不引用名称的 FETCH,但使用旧名的 APPEND FOO 会失败,并可能收到 NEWNAME 提示。邮箱内容的现实、命名空间的现实和每条连接保存的现实,此刻并不相同。

序号已经变化,通知却必须等待

RFC 2060 的消息序列号是当前邮箱中的位置,不是永久身份。一个消息被 expunge 后,后面的序号会前移。问题在于:服务器不能在回答 FETCH、STORE 或 SEARCH 的过程中插入 EXPUNGE 响应。另一个客户端已经改变了列表,当前客户端却要等到命令边界才能获知。

RFC 2180 因此承认多种处理办法。服务器可以暂存已经 expunge 的消息,先完成旧客户端的 FETCH;也可以只返回仍存在的消息,然后用 tagged NO 表示请求不完整;还可以对仍在的消息返回正常数据,对已消失的消息返回 NIL 形态的数据,并以 tagged OK 结束。另一种选择是干脆拒绝在多客户端访问时 expunge。

NIL 最能说明“格式正确”与“事实明确”的差别。空的 flags 或空的正文,既可能是数据真的为空,也可能是目标消息已经消失。客户端不能从同一个外形推导出唯一事实。存在歧义时,它应执行 NOOP,促使服务器送出等待中的 EXPUNGE,更新本地序号表,再决定是否重试。

STORE.SILENT 进一步缩小了 OK 的含义。只要所有仍存在的消息都完成修改,服务器可以回复成功,即使请求集合中有些序号已经指向被 expunge 的消息。这个回执确认的是“仍可执行的部分已经完成”,不是“最初那组消息完整存在”。

COPY 只冻结一条命令的含义

COPY 有一条更强的边界。服务器按命令开始时的序列号确定源消息;之后即使 EXPUNGE 让序号变化,成功的 COPY 仍必须把开始时确定的那些消息放进目标邮箱。如果命令失败,目标邮箱必须恢复到复制前状态。

这是一种局部的确定性,而不是邮箱快照。协议冻结的是一条命令引用的对象,不是整个邮箱的时间。它不能证明其他会话拥有同一序号表,也不能保证下一条命令仍看到同样内容。

一份实践记录,而非现行产品证明

RFC Editor 信息页将 RFC 2180 列为 Informational。文件明确说明,它既不定义 IMAP4 合规,也不穷尽所有有效行为;判断合规仍要回到 RFC 2060。它记录的是部分既有服务器的做法,以及 IMAP 邮件列表认为合理的行为。

因此,本文不能把这份 1997 年文件当成任何今天产品的实现报告。它没有测量部署率,没有证明某个服务采用幽灵邮箱,也没有给某家公司颁发安全证书。它真正留下的是一种工程纪律:客户端必须面向协议允许的行为集合,而不是只面向测试环境里那台熟悉服务器的习惯。

RFC 2177 的 IDLE 让未请求更新更及时,RFC 7162 的 CONDSTORE/QRESYNC 用修改序列和 VANISHED 降低重同步成本。它们改善的是通知与恢复,不会把一条 tagged OK 扩张成“全球状态已经同时一致”。

从现实层看,命令完成、名字消失、旧会话失去访问和字节被销毁是四个不同事实。RFC 2180 的价值,正是在协议仍允许短暂分歧时,没有让一个回执冒充全部事实。