npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

imap-sync-client

v1.1.1

Published

imap sync client, always promise

Downloads

78

Readme

node-imap-sync-client

说明

网址: https://gitee.com/linuxmail/node-imap-sync-client

同步操作 imap 客户端, 见例子 examples

本客户端将来的版本也不考虑支持 fetch bodystructure / envelope, 请慎重选择

本imap客户端, 特点:

  • 全部命令都是 promise 风格
  • 主要用于和 IMAPD 服务器同步邮箱数据和邮件数据
  • 不支持bodystructure,envelope等, 只完整下载信件, 信件解析由其他库负责
  • 支持文件夹的创建/删除/移动(改名)
  • 支持邮件的复制/移动/删除/标记/上传
  • 支持获取文件夹下邮件UID列表
  • 各种方法返回的邮箱文件夹名字都是 Buffer

接口 interface

对象初始化 选项

interface ImapSyncClientOptions extends socketSyncBuffer.options {
  user: string // 用户
  pass: string // 密码
  tryStartTLS?: boolean  // 如果服务器支持就启动 STARTTLS
  startTLS?: boolean // 是否启动 STARTTLS
  cmdIdInfo?: string // imap 命令 ID 的具体内容, 一般用于向服务器表明身份
}

回调函数类型, 记录通讯协议内容

interface ReadWriteRecordHandler {
  (type: string, data: Buffer): void   // type: read/write
}

读取一行, 解析为一组 token

export interface ReadOneLineResult {
  tokens: Buffer[]
  extraDataLength: number // 最后一个token 形如: {123}
}

命令 status 结果解析

interface MboxStatus {
  messages: number
  recent: number
  uidnext: number
  uidvalidity: number
  unseen: number
}

命令 list/lsub 返回的结果按行解析

interface MboxAttrs {
  noinferiors: boolean
  noselect: boolean
  junk: boolean
  trash: boolean
  sent: boolean
  drafts: boolean
}

命令 select 返回的结果解析

interface MboxSelect {
  exists: number
  recent: number
  uidvalidity: number
  uidnext: number
  highestmodseq: number
}

文件夹信息

interface MboxInfo {
  pathname: Buffer, // 文件夹名(Buffer), 返回的文件夹名字可能不是 imap-utf-7 编码
  mboxNameUtf8: string, // 文件夹名), 一定是 ""
  attrs: mboxAttrs
  status?: mboxStatus
  subscribed?: boolean
}

邮件标记

interface MailFlags {
  answered?: boolean // 是否已回复
  seen?: boolean // 是否已读
  draft?: boolean // 是否草稿
  flagged?: boolean // 是否标记(星标)
  deleted?: boolean // 是否删除
}

邮件标记 + 邮件 UID, 用于邮件列表

interface MailUidWithFlags extends mailFlags {
  uid: number
}

uidplus 扩展, 移动/复制/上传的结果

interface UidplusResult {
  uidvalidity: number,
  uid: number
}

使用方法

见例子: examples/imap.js

创建对象

const imapSyncClient = require("imap-sync-sclient")
let ic = new imapSyncClient.imapSyncClient({
  host: "127.0.0.1",
  port: 143,
  user: "[email protected]",
  pass: "password",
  tryStartTLS: true,
})

打开连接并初始化

打开imap连接,并认证等, 使用者可以自己实现类似的方法

// 返回 null 表示网络错误, 否则返回 boolean 值, true 表示成功
async open()

发起 STARTTLS 握手

发起命令 STARTTLS, 然后开始 ssl 握手

// 返回 null 表示网络错误, 否则返回 boolean 值, true 表示成功
async cmdStartTLS();

读取welcome

// 返回 null 表示网络错误, 否则返回 Buffer
// open() 方法内会调用这个方法
async readWelcome()

命令 capability

// 执行执行命令 capability 并返回结果, 同时保存到缓存
async forceGetCapability()
// 首先从缓存中取值
async getCapability()

登录

现在只支持 login

// 返回 null 表示网络失败, 否则返回 boolean, true表示认证成功
// open() 方法会调用这个方法
async login()

命令 ID

open() 方法会调用这个方法

//  返回 null 表示网络失败, 否则返回 boolean, true表示认证成功
// idInfo 为空则使用对象初始化参数 cmdIdInfo
async cmdId(idInfo?: string)

命令 LIST/LSUB

//  返回 null 表示网络失败, 否则返回 mboxInfo[]
async getMboxList()
async getSubscribedMboxList()
// 获取文件夹全部信息(LIST + LSUB + STATUS)
async getAllMboxInfos()

命令 create, 创建文件夹

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
async createMbox(pathname: string | Buffer)
// 创建文件夹, 并订阅
async createAndSubscribeMbox(pathname: string | Buffer)

命令 delete, 删除文件夹

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
async deleteMbox(pathname: string | Buffer)

命令 subscribe, 订阅文件夹

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
async subscribeMbox(pathname: string | Buffer)

命令 unSubscribe, 取消订阅文件夹

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
async unSubscribeMbox(pathname: string | Buffer)

命令 rename, 文件夹改名

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
async renameMbox(fromPathname: string | Buffer, toPathname: string | Buffer)

命令 select, 选择(打开)文件夹

// 返回 null 表示网络失败, 返回 false 表示不存在, 否则返回 mboxSelect
// 命令 select, 选择(打开) 文件夹
async forceSelectMbox(pathname: string | Buffer) {
// 如过select的文件夹不变,则直接返回成功
async selectMbox(pathname: string | Buffer) {

命令 UID MOVE, 移动邮件

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
// 一封
async moveOneMailByUid(uid: number | string, toPathname: string | Buffer, options?: {
  callbackForUidplus?: { (r: { uidvalidity: number, uid: number }): void }
})
// 多封
async moveMailByUid(uids: string, toPathname: string | Buffer, options?: {})

命令 MOVE, 移动邮件

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
// 一封
async moveOneMailBySn(sn: number | string, toPathname: string | Buffer, options?: {
  callbackForUidplus?: { (r: { uidvalidity: number, uid: number }): void }
})
// 多封
async moveMailBySn(sns: string, toPathname: string | Buffer, options?: {})

命令 UID COPY, 复制邮件

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
// 一封
async copyOneMailByUid(uid: number | string, toPathname: string | Buffer, options?: {
  callbackForUidplus?: { (r: { uidvalidity: number, uid: number }): void }
})
// 多封
async copyMailByUid(uids: string, toPathname: string | Buffer, options?: {})

命令 COPY, 复制邮件

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
// 一封
async copyOneMailBySn(sn: number | string, toPathname: string | Buffer, options?: {
  callbackForUidplus?: { (r: { uidvalidity: number, uid: number }): void }
})
// 多封
async copyMailBySn(sns: string, toPathname: string | Buffer, options?: {})

命令 UID STORE, 设置标记

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
async setMailFlagByUid(uidOrUids: number | string, flags: mailFlags, set_or_unset?: boolean)
async unsetMailFlagByUid(uidOrUids: number | string, flags: mailFlags)

命令 STORE, 设置标记

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
async setMailFlagBySn(snOrSns: number | string, flags: mailFlags, set_or_unset?: boolean)
async unsetMailFlagBySn(snOrSns: number | string, flags: mailFlags)

删除信件, UID

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
async deleteMailByUid(uidOrUids: number | string)

删除信件

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
async deleteMailBySn(snOrSns: number | string)

获取邮件列表 UID + 标记

// 返回 null 表示网络失败, 否则返回 mailUidWithFlags[]
async fetchUidListWithFlags()

通过搜索命令, 获取邮件 UID 列表

// 返回 null 表示网络失败, 否则返回 number[]

//  全部邮件
async searchAllUids()
// 全部未读邮件
async searchUnseenUids()
// 全部已回复邮件
async searchAnsweredUids()
// 全部设置了已删除标记的邮件
async searchDeletedUids()
// 全部草稿邮件
async searchDraftUids()
// 全部flagged(星标)邮件
async searchFlaggedUids()

命令 append, 上传信件

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
// callbackForMailPieceData, 多次调用, 返回上传的信件的部分数据,读够mailSize就不在执行
// options.callbackForUidplus, 如果支持 uidplus 协议, 则执行
async appendMail(mboxname: Buffer | string, mailSize: number,
  callbackForMailPieceData: { (): Promise<Buffer | null> },
  options?: {
    flags?: mailFlags
    date?: any /* string, unix-time, Date */
    callbackForUidplus?: { (r: uidplusResult): void }
  })

imap 返回结果 OK/NO/BAD

// 返回 boolean
resultIsOk()
resultIsNo()
resultIsBad()

编译字符串

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命令可以靠这个基础封装实现

// 返回 null 表示网络失败, 否则返回 boolean, true表示成功
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

// 读一行返回,并解析为 tokens
async readOneLineTokens()
// 读取一个完整的返回, 并解析为 tokens
async readTokens()
// 解析返回结果是不是 OK/NO/BAD
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")

// 字符集转码: imap-utf-7 => utf-8 
function imapSyncClient.imapUtf7ToUtf8(str: string | Buffer): string

// 字符集转码: utf-8 => imap-utf-7
function imapSyncClient.utf8ToImapUtf7(str: string | Buffer): string