发布SwiftNIO IMAP的详细指南

119 阅读5分钟

作为扩大 Swift on Server 生态系统的一部分,我们很高兴地宣布发布一个新的 IMAPv4 解析器和编码器,即 SwiftNIO IMAP。

这个软件包实现了:

  • 将 IMAPv4 线程格式解析为类型安全的 Swift 数据结构
  • 将这些 Swift 数据结构编码为 IMAPv4 线路格式
  • 对常见的 IMAP 扩展的广泛支持
  • 高性能
  • 与 SwiftNIO 集成

动机

40 多年来,电子邮件一直是互联网不可或缺的一部分,是当今许多产品和服务中无处不在的一部分。

互联网信息访问协议(IMAP)是最广泛使用的检索电子邮件信息的开放标准。它已经存在了几十年,并通过各种RFCs在这些年里有了很大的发展。

正确地解析和编码 IMAP 是出了名的困难。SwiftNIO IMAP 通过处理编码和解析 IMAP 的许多微妙细节降低了这一难度,使得在服务器上用 Swift 编写丰富而强大的电子邮件集成比以往任何时候都更容易。

这个包专注于解析和编码IMAP,同时也提供了一些与核心IMAP类型相关的常用便利方法。它不实现任何与 IMAP 相关的总线逻辑。

简要浏览

SwiftNIO IMAP 的目标是 RFC 3501,即 IMAP 版本 4rev1,此外还支持 20 多个 RFC 的扩展。RFC 2087、2177、2221、2342、2971、3348、3501、3502、3516、3691、4315、4467、4469、4731、4959、5032、5161、5182、5258、5464、5819、6154、6851、7162、7377、7888 和 8438。

IMAP使用基于文本的 "人类可读 "电线格式,SwiftNIO IMAP使用现代Swift数据结构将其连接到类型安全的世界。该协议是 "非对称的":从服务器发送的消息与客户端发送的消息遵循不同的模式。

交换实例

作为一个快速的例子,这里是RFC 3501第8节中列出的交换的一部分,其中以S:C: 开始的行分别来自服务器和客户端:

S:   * OK IMAP4rev1 Service Ready
C:   a001 login mrc secret
S:   a001 OK LOGIN completed
C:   a002 select inbox
S:   * 18 EXISTS
S:   * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
S:   * 2 RECENT
S:   * OK [UNSEEN 17] Message 17 is the first unseen message
S:   * OK [UIDVALIDITY 3857529045] UIDs valid
S:   a002 OK [READ-WRITE] SELECT 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(.mailboxData(.recent(2)))
Response.untagged(.conditionalState(.ok(ResponseText(code: .unseen(17), text: "Message 17 is the first unseen message"))))
Response.untagged(.conditionalState(.ok(ResponseText(code: .uidValidity(3857529045), text: "UIDs valid"))))
Response.tagged(.init(tag: "a002", state: .ok(ResponseText(code: .readWrite, text: "SELECT completed"))))

这里发生的事情比这个例子显示的要多。但这让我们大致了解了事情的外观和感觉。

常见值

IMAP 中使用的一些非常常见的值是 UID、消息序列号和邮箱名称。

SwiftNIO IMAP 有一个UIDSequenceNumber 类型,以及相关类型,如UIDRange,UIDSet,SequenceRange, 和SequenceSet 。这两个集合类型符合SetAlgebra 。而且所有这些都有方便的方法来进行常见的操作。

信箱是由一个 "修改过的UTF-7 "编码的字符串来识别的。MailboxNameMailboxPath 类型支持对这些进行解码和编码,同时允许对有时在野外发现的错误编码的字节串进行往返处理。

透明的字面意义支持

SwiftNIO IMAP 可以透明地对客户端消息进行编码和解码,包括来自基本 RFC 3501 和 RFC 7888 扩展的同步和非同步字面编码。

这些变化被透明地处理--对客户和服务器都是如此。

RFC 3501 "引号 "字符串:

C: A001 LOGIN "FRED FOOBAR" "fat man"
S: A001 OK LOGIN completed

RFC 3501命令的延续:

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 提供了一对ChannelHandler 对象,可以集成到 SwiftNIOChannelPipeline 中。这允许使用 NIOChannels 发送 IMAP 命令。

SwiftNIO IMAP 提供的两个处理程序是IMAPClientHandlerIMAPServerHandler 。每一个都可以插入到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)

ChannelHandlers 支持透明字面、IMAP 能力以及 SwiftNIO IMAP 的所有其他功能。它们是服务器和客户端的 IMAP 应用程序的强大构建模块。

下一步是什么

我们今天发布的 SwiftNIO IMAP 版本仍然是一个原型。我们希望征求社区的反馈意见。

我们已经对项目的代码进行了广泛的测试,我们相信它已经接近于 "生产就绪"。但是我们很希望在Swift论坛上进行设计讨论,讨论缺少什么,以及哪些地方可以改进。

参与进来

我们非常欢迎你的反馈、经验和贡献: