node-imap-sync-client
说明
网址: https://github.com/mailhonor/node-imap-sync-client
同步操作 imap 客户端, 见例子 examples
本imap客户端, 特点:
- 全部命令都是 promise 风格
- 主要用于和 IMAPD 服务器同步邮箱数据和邮件数据
- 支持文件夹的创建/删除/移动(改名)
- 支持邮件的复制/移动/删除/标记/上传
- 支持获取文件夹下邮件UID列表
- 读取邮件, 信封, 结构, 附件等
- 各种方法返回的邮箱文件夹名字都是 Buffer
接口 Type
对象初始化 选项
export type ImapSyncClientOptions = {
user: string
pass: string
tryStartTLS?: boolean
startTLS?: boolean
cmdIdInfo?: string
} & socketSyncBuffer.options
回调函数类型, 记录通讯协议内容
export type ReadWriteRecordHandler = (
type: string,
data: Buffer) => void
读取一行, 解析为一组 token
export type ResponseToken = {
value: Buffer,
children: ResponseToken[]
}
export type ReadOneLineResult = {
tokens: ResponseToken[],
extraDataLength: number,
depth: number,
}
命令 status 结果解析
export type MboxStatus = {
messages: number
recent: number
uidnext: number
uidvalidity: number
unseen: number
}
命令 list/lsub 返回的结果按行解析
export type MboxAttrs = {
noinferiors: boolean
noselect: boolean
hierarchySplitor: string,
inbox: boolean
junk: boolean
trash: boolean
sent: boolean
drafts: boolean
}
命令 select 返回的结果解析
export type MboxSelect = {
exists: number
recent: number
uidvalidity: number
uidnext: number
highestmodseq: number
}
文件夹信息
export type MboxInfo = {
mboxName: Buffer,
mboxNameUtf8: string,
attrs: MboxAttrs
status?: MboxStatus
subscribed?: boolean
}
邮件标记
export type MailFlags = {
answered?: boolean
seen?: boolean
draft?: boolean
flagged?: boolean
deleted?: boolean
}
邮件标记 + 邮件 UID, 用于邮件列表
export type MailUidWithFlags = {
uid: number
} & MailFlags
uidplus 扩展, 移动/复制/上传的结果
export type UidplusResult = {
uidvalidity: number,
uid: number
}
信封
export type EnvelopeAddress = {
name: Buffer;
nameUtf8: string;
address: string;
}
export type EnvelopeAttrs = {
uid: number,
size: number,
date: string,
subject: Buffer,
subjectUtf8: string,
from: EnvelopeAddress | null,
sender: EnvelopeAddress | null,
to: EnvelopeAddress[] | null,
cc: EnvelopeAddress[] | null,
bcc: EnvelopeAddress[] | null,
replyTo: EnvelopeAddress[] | null,
inReplyTo: string,
messageId: string,
flags: MailFlags,
}
结构
export type BodyStructure = {
textMimes: MimeNode[],
showMimes: MimeNode[],
attachmentMimes: MimeNode[],
topMime: MimeNode,
}
信封 + 结构
export type MailInfo = {
} & BodyStructure & Envelope
使用方法
见例子: examples/imap.js
创建对象
const imapSyncClient = require("imap-sync-sclient")
let ic = new imapSyncClient.imapSyncClient({
host: "127.0.0.1",
port: 143,
user: "test@linuxmail.cn",
pass: "password",
tryStartTLS: true,
})
打开连接并初始化
打开imap连接,并认证等, 使用者可以自己实现类似的方法
async open()
发起 STARTTLS 握手
发起命令 STARTTLS, 然后开始 ssl 握手
async cmdStartTLS();
读取welcome
async readWelcome()
命令 capability
async forceGetCapability()
async getCapability()
登录
现在只支持 login
async login()
命令 ID
open() 方法会调用这个方法
async cmdId(idInfo?: string)
命令 LIST/LSUB
async getMboxList()
async getSubscribedMboxList()
async getAllMboxInfos()
命令 create, 创建文件夹
async createMbox(pathname: string | Buffer)
async createAndSubscribeMbox(pathname: string | Buffer)
命令 delete, 删除文件夹
async deleteMbox(pathname: string | Buffer)
命令 subscribe, 订阅文件夹
async subscribeMbox(pathname: string | Buffer)
命令 unSubscribe, 取消订阅文件夹
async unSubscribeMbox(pathname: string | Buffer)
命令 rename, 文件夹改名
async renameMbox(fromPathname: string | Buffer, toPathname: string | Buffer)
命令 select, 选择(打开)文件夹
async forceSelectMbox(pathname: string | Buffer)
async selectMbox(pathname: string | Buffer)
命令 UID MOVE, 移动邮件
async moveOneMail(uid: number | string, toPathname: string | Buffer, options?: {
callbackForUidplus?: { (r: { uidvalidity: number, uid: number }): void }
})
async moveMail(uids: string, toPathname: string | Buffer, options?: {})
命令 UID COPY, 复制邮件
async copyOneMail(uid: number | string, toPathname: string | Buffer, options?: {
callbackForUidplus?: { (r: { uidvalidity: number, uid: number }): void }
})
async copyMail(uids: string, toPathname: string | Buffer, options?: {})
命令 UID STORE, 设置标记
async setMailFlag(uidOrUids: number | string, flags: mailFlags, set_or_unset?: boolean)
async unsetMailFlag(uidOrUids: number | string, flags: mailFlags)
删除信件, UID
async deleteMail(uidOrUids: number | string)
获取邮件列表 UID + 标记
async fetchUidListWithFlags()
通过搜索命令, 获取邮件 UID 列表
async searchAllUids()
async searchUnseenUids()
async searchAnsweredUids()
async searchDeletedUids()
async searchDraftUids()
async searchFlaggedUids()
搜索
async searchMail(querys: string)
命令 append, 上传信件
async appendMail(mboxname: Buffer | string, mailSize: number,
callbackForMailPieceData: { (): Promise<Buffer | null> },
options?: {
flags?: mailFlags
date?: any
callbackForUidplus?: { (r: uidplusResult): void }
})
获取一封信件的信封信息
async fetchEnvelope(uid: string | number): Promise<EnvelopeAttrs | false | null>
获取一封信件的结构信息
async fetchMailStructure(uid: number | string): Promise<BodyStructure | false | null>
获取一封信件的信息(信封+结构)
async fetchMailInfo(uid: number | string): Promise<MailInfo | false | null>;
获取一封信件
async fetchMailData(uid: number,
callbackForMailPieceData: { (pieceData: Buffer): Promise<boolean> },
options?: {
partial?: {
offset: number,
length: number,
},
}
)
获取一封信件的信头
async fetchMailHeader(uid: number,
callbackForMailPieceData: { (pieceData: Buffer): Promise<boolean> },
options?: {
partial?: {
offset: number,
length: number,
},
}
)
获取一封信件部分的数据
async fetchMimeDataBySection(uid: number | string,
section: string,
callbackForMailPieceData: { (pieceData: Buffer): Promise<boolean> },
options?: {
partial?: {
offset: number,
length: number,
},
})
async fetchMimeHeaderBySection(uid: number | string,
section: string,
callbackForMailPieceData: { (pieceData: Buffer): Promise<boolean> },
options?: {
partial?: {
offset: number,
length: number,
},
})
imap 返回结果 OK/NO/BAD
resultIsOk()
resultIsNo()
resultIsBad()
也许服务器有新的信件, 回调
setMaybeHaveNewMailHandler(handler: (pathname: Buffer) => any)
编译字符串
escape(str: string | Buffer): string | Buffer
escape("a\nb\"c") => "a\\nb\"c"
escape("a\nb\"c") => {5}
a
b"c
其他
setDebugMode(tf = true)
setReadWriteRecordHandler(handler: readWriteRecordHandler)
getLastReadedBuffer(): Buffer
isNetError()
isLogicError()
isPasswordError()
扩展(基础) API
通用 IMAP 命令 封装
大部分IMAP命令可以靠这个基础封装实现
async generalCmd(cmdArgv: (Buffer | string)[], options?: {
callbackForUntag?: { (data: Buffer[]): Promise<void> }
callbackForTag?: { (data: Buffer[]): Promise<void> }
[keys: string]: any
})
例如:
async _searchUidsByFlag(flag: string) {
let uids: number[] = []
let res = await this.generalCmd(["UID SEARCH ", flag], {
callbackForUntag: async (tokens: Buffer[]) => {
let i;
for (i = 2; i < tokens.length; i++) {
uids.push(parseInt(tokens[i].toString()))
}
},
})
if (!res) {
return null
}
return uids
}
读取行数据,并解析为 tokens
async readOneLineTokens()
async readTokens()
parseResult(tokens: Buffer[]): boolean
读写原始socket数据
见 this.socket, 见模块 socket-sync-buffer
字符集转码
见过太多不规范的文件夹名字, 以 "研发部" 为例子
合法的(imap-utf-7): &eBRT0ZDo-
不规范的(imap-utf-7): &eBRT0D-
非法的(utf-7): 研发部
非法的(GBK): 研发部
本库作者认为, 库不可能自动正确处理这些文件夹名字的解码, 而只是返回Buffer.
不做进一步的转码工作, 以保证通过 Buffer 能正确的操作这些文件夹
而文件夹的名字要最终转为UTF-8用于显示,使用者需要自己承担乱码的风险, 建议通过库 jschardet 来自动识别字符集
下面是规范的字符集转码方法:
const imapSyncClient = require("imap-sync-sclient")
function imapSyncClient.imapUtf7ToUtf8(str: string | Buffer): string
function imapSyncClient.utf8ToImapUtf7(str: string | Buffer): string