作为扩大 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 有一个UID
和SequenceNumber
类型,以及相关类型,如UIDRange
,UIDSet
,SequenceRange
, 和SequenceSet
。这两个集合类型符合SetAlgebra
。而且所有这些都有方便的方法来进行常见的操作。
信箱是由一个 "修改过的UTF-7 "编码的字符串来识别的。MailboxName
和MailboxPath
类型支持对这些进行解码和编码,同时允许对有时在野外发现的错误编码的字节串进行往返处理。
透明的字面意义支持
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
中。这允许使用 NIOChannel
s 发送 IMAP 命令。
SwiftNIO IMAP 提供的两个处理程序是IMAPClientHandler
和IMAPServerHandler
。每一个都可以插入到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
s 支持透明字面、IMAP 能力以及 SwiftNIO IMAP 的所有其他功能。它们是服务器和客户端的 IMAP 应用程序的强大构建模块。
下一步是什么
我们今天发布的 SwiftNIO IMAP 版本仍然是一个原型。我们希望征求社区的反馈意见。
我们已经对项目的代码进行了广泛的测试,我们相信它已经接近于 "生产就绪"。但是我们很希望在Swift论坛上进行设计讨论,讨论缺少什么,以及哪些地方可以改进。
参与进来
我们非常欢迎你的反馈、经验和贡献:
- 请在GitHub 上试用 SwiftNIO IMAP 库。
- 在Swift论坛上讨论该库,提出改进建议并获得帮助。
- 为你所发现的问题开立议题。
- 贡献拉动请求以修复任何问题。