要約

  • IMAP の CLOSE は Selected から Authenticated へ戻るだけの命令ではない。書き込み可能なメールボックスで実行すると、\Deleted の付いた全メッセージを恒久的に除去し、個別の EXPUNGE 応答は返さない。
  • RFC 3691 は UNSELECT を追加した。同じように選択中の資源を解放して状態を戻すが、expunge は行わない。状態遷移と不可逆な変更を別の動詞にしたことが、この短い拡張の核心である。

あるクライアントが受信箱の同期を終えた。TCP 接続も認証も維持したいが、サーバーに選択中メールボックスの状態を持たせ続ける必要はない。画面上の意図は単純である。いまの箱から離れたいだけだ。

ところが、そのメールボックスには \Deleted フラグの付いたメッセージが残っているかもしれない。直前にこのクライアントが付けたものとは限らない。別のセッションが変更した可能性も、利用者が最終判断を保留している可能性もある。

初期の IMAP で最も自然に見える出口は CLOSE だった。名前は資源の片付けを思わせるが、プロトコル上の作用は二つある。選択状態を終えて Authenticated に戻す。それと同時に、書き込み可能なメールボックスにある全 \Deleted メッセージを恒久的に除去する。

一つの動詞が、作業場所からの退出と保存データの確定処分を兼ねていた。

2004 年の RFC 3691 は、この二つを切り分ける UNSELECT を定義した。命令に引数はない。成功すれば選択中メールボックスに結び付いたサーバー資源を解放し、接続を Authenticated に戻す。しかしメッセージは一件も恒久削除しない。

小さな追加だが、修正したのは深い境界だった。状態機械の同じ終点へ向かう二本の辺が、データに対して異なる権限を持つことを、呼び出し側が明示できるようになった。

Selected は画面の選択ではなく通信上の契約だった

IMAP の接続には状態がある。認証後のクライアントはメールボックスを列挙できるが、メッセージを検索し、取得し、フラグを変更し、コピーし、削除するには対象のメールボックスを選択する。Selected 状態では、サーバーも件数やフラグ変化など、その箱に関する文脈を維持する。

この文脈を終えることと、ログアウトすることは同じではない。クライアントは Inbox を離れて Drafts を開くかもしれない。同期処理は一度資源を返し、同じ認証済み接続で後から別の作業をするかもしれない。Selected から Authenticated への遷移は、それ自体で正当な操作である。

CLOSE はその遷移を実現した。ただし状態図は、辺の途中でメッセージに何が起きたかを十分には表さない。遷移後に同じ Authenticated に着いても、一方では保存物が失われ、他方では残ることがある。

\Deleted も「すでに消えた」という意味ではない。これは後で除去するための印であり、expunge が恒久削除を実行する。印を付ける段階と確定する段階を分けるから、利用者は複数の候補を見直せる。一方で、印を付けた主体と最後に expunge を起動する主体が違うことも起こる。

CLOSE が退出と expunge を一緒に担うと、いちばん事務的に見える最後の命令が、残っていた印すべてを確定させてしまう。

応答を省くことと、作用を省くことは違う

通常の EXPUNGE は、\Deleted の付いたメッセージを恒久削除し、除去ごとに untagged EXPUNGE 応答を送る。そこに現れる message sequence number は、選択中メールボックスにおける位置である。前のメッセージが消えれば後ろの番号が詰まるため、複数の応答は移動する位置を順に伝える。

CLOSE はこれらの個別応答を送らない。クライアントは直後にメールボックスを離れるので、位置更新を利用しない場合が多い。削除対象が多ければ、応答を生成して伝送する負荷を省ける。標準が示す性能上の理由は合理的だった。

しかし、通信量が少ないから非破壊的なのではない。tagged OKCLOSE の完了を示すが、削除なしを証明しない。しかも、どのメッセージが消えたかを一件ずつ示す台帳でもない。短い応答ほど安全だという直感は、ここでは逆になる。

読み取り専用の選択にも注意が要る。EXAMINE で開いた箱などでは、CLOSE を送ってもメッセージは除去されず、そのためのエラーも返らない。同じ命令と成功応答でも、選択モードによって恒久効果が異なる。

したがって、後から意味を判断するには命令名だけでは足りない。メールボックス、読み書きモード、実行前の \Deleted 集合、選んだ出口、応答の到達まで結び付ける必要がある。

古い仕様にも削除しない抜け道はあった

IMAP4rev1 では、すべての退出に CLOSE を要求していなかった。現在の箱を閉じる前に別の SELECTEXAMINE を送ることも、LOGOUT することもできた。それらは選択を暗黙に終えるが、expunge はしない。

つまり、非削除の結果自体は可能だった。欠けていたのは、認証を保ったまま選択だけ終えるための素直な表現である。RFC 3691 は当時の回避策を記録している。一つは、存在しないメールボックスをわざと SELECT し、選択失敗による状態遷移を利用する方法。もう一つは、同じ箱を EXAMINE で選び直す方法だった。

前者は失敗を正常系に埋め込む。サーバーログでは意図された退出と本当の名前間違いが同じに見え、失敗率も汚れる。後者は手放したい文脈を別のモードで開き直す。どちらも到達状態は得られるが、クライアントの意思を正面から述べない。

隠れた制御フローは互換性の負債になる。実装者が不要に見える失敗動作を整理すると、そこを出口として使うクライアントを壊すかもしれない。運用者は意図をログから復元できない。

必要だったのは、新しい回避策ではなく「選択を解き、メッセージには触れない」という一等の命令だった。

capability は推測の代わりに共通語を作った

RFC 3691 の拡張を理解する IMAP4rev1 サーバーは UNSELECT capability を示す。クライアントは送信前に相手がこの動詞を理解すると確認できた。未知命令が拒否されたことを、安全な退出の成功とみなすことはできない。失敗後も Selected のままかもしれず、資源も残っているかもしれないからだ。

能力交渉は、製品名や実装年代から挙動を推測する必要を減らした。双方が語彙を共有してから、その語彙に結び付いた狭い意味を実行する。ここで capability が証明するのは命令理解であり、操作者の正当性、画面表示の正確さ、メール内容の真正性ではない。

UNSELECT 自体の応答は簡潔である。専用の untagged 応答を必要とせず、成功すれば tagged completion が返る。保護は長い証明書から生まれるのではない。命令定義が、永久削除の権限を含まないことから生まれる。

もし結果を受け取る前に接続が切れれば、クライアントは状態を推測してはいけない。それでも UNSELECT の再試行がデータ削除を再実行しないという点は、CLOSE の不確実な再送より扱いやすい。副作用の幅が狭いほど、失敗後の判断も狭くできる。

同じ状態遷移でも同じ命令ではない

CLOSEUNSELECT はどちらも Selected から Authenticated に進む。次にどの種類の命令を送れるかだけ見れば、同じ結果である。保存データに何をしたかを見れば、交換可能ではない。

分散システムでは、この見落としが繰り返される。データベース接続は commit 後にも rollback 後にも閉じる。ファイルは flush 後にもバッファ破棄後にも release される。リースは返却でも期限切れでも終わる。最終状態だけを記録すると、経路を選んだ権限と結果の証拠が消える。

運用記録も分けるべきである。selection_releasedexpunge_requestedexpunge_effect_observedtagged_result_received は別の事実だ。一つの closed フラグに集約すると、障害時に保存と削除を区別できない。

この観点で見ると、UNSELECT は単なる利便命令ではない。状態遷移のために、関係のない破壊権限を渡さなくて済むようにした最小権限の設計である。

UNSELECT は過去を取り消さない

UNSELECT は既存の \Deleted フラグを消さない。すでに expunge されたメッセージを戻さず、誰が印を付けたかも説明しない。再び箱を選べば印が残っている可能性があり、後の EXPUNGECLOSE、または対象を限定した操作で恒久削除され得る。

したがって、この命令が守るのはメッセージの永続保存ではなく、判断点である。いま退出することだけを理由に、保留中の印を確定しない。並行セッションの変更を止めるトランザクションでもない。

狭い保証を広げて語らないことが重要だ。UNSELECT の約束は明確で検証可能である。この命令による状態退出は expunge を伴わない。それ以上の保持方針や復旧能力は、別の層が所有する。

IMAP4rev2 は区別を基本仕様にした

RFC 9051 の IMAP4rev2 では、UNSELECT が Selected 状態の基本命令に入った。状態説明は CLOSEUNSELECT の双方を Authenticated への遷移として扱う。それでも各命令のデータ効果は統合されない。CLOSE は書き込み可能な箱の \Deleted を恒久除去し、UNSELECT は除去しない。

拡張から基礎仕様への移行は、互換性を保ちながら意味を修復する一つの型を示す。古い方法をなかったことにせず、クライアントが本来の意思を明示できる道を標準にする。エラー経路への依存が減り、呼び出し箇所のレビューもログの解釈も容易になる。

インターネット史では新しい能力の追加が注目されやすい。UNSELECT が加えた価値は、むしろ不要な能力を一つ外したことにある。資源を返す要求は資源を返すだけでよい。削除を確定する権限は、削除を意味する別の選択から得なければならない。

出典