この記事の要点
- Swift サーバーエコシステム向けの新しいオープンソースプロジェクト SwiftNIO IMAP(swift-nio-imap)が発表されました。メール取得の標準プロトコルである IMAPv4 のパーサとエンコーダを提供する Swift パッケージです。
- IMAP のワイヤーフォーマット(テキストベースの通信形式)と、型安全な Swift のデータ構造との相互変換を担います。IMAP に関するビジネスロジックは実装せず、あくまでパースとエンコードに専念します。
- RFC 3501(IMAP version 4rev1)を対象とし、20 を超える RFC による一般的な IMAP 拡張を広くサポートします。SwiftNIO の
ChannelHandlerとして組み込める形でも提供されます。 - 発表時点ではプロトタイプ段階で、コミュニティからのフィードバックを募っています。
何が発表されたのか
SwiftNIO IMAP は、IMAPv4 のパーサとエンコーダです。次の機能を実装します。
- IMAPv4 のワイヤーフォーマットを型安全な Swift のデータ構造へパースする
- それらの Swift のデータ構造を IMAPv4 のワイヤーフォーマットへエンコードする
- 一般的な IMAP 拡張への幅広いサポート
- 高い性能
- SwiftNIO との統合
メール(Email)は 40 年以上にわたってインターネットに不可欠な存在であり、IMAP(Internet Message Access Protocol)はメールメッセージを取得するための最も広く使われているオープン標準です。長い歴史の中でさまざまな RFC を通じて大きく進化してきた一方、IMAP を正しくパース・エンコードするのは難しいことで知られています。SwiftNIO IMAP は、エンコードとパースの細かく込み入った部分を肩代わりすることでこの難しさを減らし、Swift on Server でリッチで強力なメール連携を書きやすくします。
このパッケージはパースとエンコードに焦点を当てつつ、IMAP の中核的な型に関するいくつかの便利なメソッドも提供します。IMAP に関わるビジネスロジックは実装しません。
何に使えるのか
SwiftNIO IMAP は RFC 3501(IMAP version 4rev1)を対象とし、加えて 20 以上の RFC(RFC 2087・2177・2342・2971・3502・4315・4959・5258・6154・7888 など)による拡張をサポートします。
IMAP は「人間が読める」テキストベースのワイヤーフォーマットを使いますが、SwiftNIO IMAP はこれをモダンな Swift のデータ構造による型安全な世界へ橋渡しします。プロトコルは「非対称」で、サーバーが送るメッセージとクライアントが送るメッセージは異なるパターンに従います。
やり取りの例
たとえば RFC 3501 section 8 のやり取りの一部は次のようになります(S: がサーバー、C: がクライアント)。
S: * OK IMAP4rev1 Service Ready
C: a001 login mrc secret
S: a001 OK LOGIN completed
この 3 行は SwiftNIO IMAP では次のように表されます。
Response.untagged(.conditionalState(.ok(ResponseText(text: "IMAP4rev1 Service Ready"))))
CommandStreamPart.tagged(TaggedCommand(tag: "a001", command: .login(username: "mrc", password: "secret")))
Response.tagged(.init(tag: "a001", state: .ok(ResponseText(text: "LOGIN completed"))))
続く SELECT コマンドとその応答のように、より構造的なやり取りも型として表現されます。
CommandStreamPart.tagged(TaggedCommand(tag: "a002", command: .select(MailboxName("box1"), [])))
Response.untagged(.mailboxData(.exists(18)))
Response.untagged(.mailboxData(.flags([.answered, .flagged, .deleted, .seen, .draft])))
Response.untagged(.conditionalState(.ok(ResponseText(code: .unseen(17), text: "Message 17 is the first unseen message"))))
Response.tagged(.init(tag: "a002", state: .ok(ResponseText(code: .readWrite, text: "SELECT completed"))))
中核となる型
IMAP で頻繁に使われる値には UID・メッセージのシーケンス番号・メールボックス名があります。
- SwiftNIO IMAP は
UID型とSequenceNumber型、およびUIDRange・UIDSet・SequenceRange・SequenceSetといった関連型を持ちます。2 つのセット型はSetAlgebraに適合し、いずれの型にも一般的な操作のための便利なメソッドが用意されています。 - メールボックスは「modified UTF-7」でエンコードされた文字列で識別されます。
MailboxNameとMailboxPathがこのエンコード・デコードをサポートし、実環境でときに見られる誤ってエンコードされたバイト列も往復(round-trip)できるようになっています。
リテラルの透過的なサポート
SwiftNIO IMAP は、RFC 3501 の同期(synchronizing)リテラルと、RFC 7888 拡張の非同期(non-synchronizing)リテラルの両方を、クライアント・サーバーのいずれについても透過的にエンコード・デコードできます。
たとえば次の 3 つの書き方は、いずれも透過的に扱われます。
RFC 3501 の「quoted」文字列:
C: A001 LOGIN "FRED FOOBAR" "fat man"
S: A001 OK LOGIN completed
RFC 3501 のコマンド継続(command continuation):
C: A001 LOGIN {11}
S: + Ready for additional command text
C: FRED FOOBAR {7}
S: + Ready for additional command text
C: fat man
S: A001 OK LOGIN completed
RFC 7888 の非同期リテラル:
C: A001 LOGIN {11+}
C: FRED FOOBAR {7+}
C: fat man
S: A001 OK LOGIN completed
いわゆる LITERAL+ / LITERAL- のサポートは、サーバーからの CAPABILITY 応答によって有効化するか、エンコードオプションを明示的に設定して有効化できます。
SwiftNIO との統合
SwiftNIO IMAP は、SwiftNIO の ChannelPipeline に組み込める 1 組の ChannelHandler を提供します。これにより、NIO の Channel を通じて IMAP コマンドを送受信できます。
提供されるハンドラは IMAPClientHandler と IMAPServerHandler の 2 つです。ChannelPipeline に挿入すると、メッセージのエンコード・デコードに使えます。
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let channel = try await ClientBootstrap(group).channelInitializer { channel in
channel.pipeline.addHandler(IMAPClientHandler())
}.connect(host: example.com, port: 143).get()
try await channel.writeAndFlush(CommandStreamPart.tagged(TaggedCommand(tag: "a001", command: .login(username: "mrc", password: "secret"))), promise: nil)
これらの ChannelHandler は透過的なリテラル・IMAP のケイパビリティ(capability)など SwiftNIO IMAP の全機能をサポートし、サーバー側・クライアント側いずれの IMAP アプリケーションにとっても強力な構成要素になります。
導入・今後の位置づけ
発表時点で公開されている SwiftNIO IMAP はまだプロトタイプ段階で、コミュニティからのフィードバックを募っています。コードは入念にテストされており「production ready」に近いと考えられていますが、何が足りないか、どこを改善できるかについて、Swift フォーラムでの設計議論を歓迎するとされています。
SwiftNIO IMAP は GitHub 上で開発されているオープンソースプロジェクトです。議論や改善提案、質問は Swift フォーラムで行え、問題があれば issue を、修正があればプルリクエストを送れます。