swift-nio-imap

一个 Swift 项目,它提供 IMAP4rev1 协议的实现,构建于 SwiftNIO 之上。

此项目实现以下功能:

有关 IMAP 扩展的列表,请参见 EXTENSIONS.md

⚠️ 注意: 此库仍处于开发阶段,尚未准备好在生产系统中使用。⚠️

简介和用法

swift-nio-imap 实现了 RFC 3501 和相关 RFC 中描述的 IMAP4rev1 协议。 它旨在作为构建邮件客户端和/或服务器的基础。 它构建于 SwiftNIO v2.x 之上。

要使用该框架,请使用 import NIOIMAP。 NIOIMAP 支持各种 IMAP 扩展,请查看 EXTENSIONS.md 获取完整详细信息。

示例交换

作为一个快速示例,以下是 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"))))

这里发生的事情比此示例显示的更多。 但这给出了类型外观和感觉的一般概念。

与 SwiftNIO 集成

SwiftNIO IMAP 提供了一对可以集成到 SwiftNIO ChannelPipeline 中的 ChannelHandler 对象。 这允许使用 NIO Channels 发送 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)