class Net::IMAP

Net::IMAP 实现了 Internet 消息访问协议 (IMAP) 客户端功能。该协议在 IMAP4rev1 [RFC3501]IMAP4rev2 [RFC9051] 中进行了描述。

IMAP 概述

IMAP 客户端连接到服务器,然后使用 authenticatelogin 进行身份验证。完成身份验证后,可以使用一系列命令。大多数命令处理邮箱,邮箱可以排列在分层命名空间中,每个邮箱包含零个或多个消息。这在服务器上的实现方式取决于实现;在 UNIX 服务器上,通常会将其实现为目录层次结构中的邮箱格式的文件。

要处理邮箱中的消息,客户端必须先使用 selectexamine(用于只读访问)选择该邮箱。客户端成功选择邮箱后,将进入“selected”状态,并且该邮箱将成为当前邮箱,与邮件项相关的命令会隐式地在该邮箱上操作。

序列号和 UID

消息有两种类型的标识符:消息序列号和 UID。

消息序列号从 1 开始对邮箱内的消息进行编号,直到邮箱中的项目数。如果会话期间收到新消息,则该消息的序列号等于邮箱的新大小。如果从邮箱中删除消息,则剩余消息的序列号将“向下洗牌”以填补空白。

为避免序列号竞争条件,服务器在没有命令正在进行时,或者在响应 fetchstoresearch 时,不得删除消息。删除可能在任何其他命令期间发送,包括 uid_fetchuid_storeuid_searchnoopidle 命令都对此副作用很有用:它们允许服务器发送所有邮箱更新,包括删除。

另一方面,UID 永久保证不会识别同一邮箱内的另一条消息,即使现有消息被删除也是如此。UID 必须在邮箱内按升序(但不一定是连续)顺序分配;这意味着如果非 IMAP 客户端重新排列邮箱内邮件项的顺序,则必须重新分配 UID。因此,IMAP 客户端无法重新排列消息顺序。

使用示例

列出默认邮箱中所有最近邮件的发送者和主题

imap = Net::IMAP.new('mail.example.com')
imap.authenticate('PLAIN', 'joe_user', 'joes_password')
imap.examine('INBOX')
imap.search(["RECENT"]).each do |message_id|
  envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]
  puts "#{envelope.from[0].name}: \t#{envelope.subject}"
end

将 2003 年 4 月的所有邮件从“Mail/sent-mail”移动到“Mail/sent-apr03”

imap = Net::IMAP.new('mail.example.com')
imap.authenticate('PLAIN', 'joe_user', 'joes_password')
imap.select('Mail/sent-mail')
if not imap.list('Mail/', 'sent-apr03')
  imap.create('Mail/sent-apr03')
end
imap.search(["BEFORE", "30-Apr-2003", "SINCE", "1-Apr-2003"]).each do |message_id|
  imap.copy(message_id, "Mail/sent-apr03")
  imap.store(message_id, "+FLAGS", [:Deleted])
end
imap.expunge

功能

大多数 Net::IMAP 方法目前不会根据服务器通告的 capabilities 修改其行为。此类用户必须在发送扩展命令或命令参数之前检查服务器是否能够支持它们。应特别注意遵循 capabilitiesstarttlsloginauthenticate 的要求。

请参阅 capable?auth_capable?capabilitiesauth_mechanisms 以发现服务器功能。有关相关功能要求,请参阅每个 IMAP 命令的文档。

imap = Net::IMAP.new("mail.example.com")
imap.capable?(:IMAP4rev1) or raise "Not an IMAP4rev1 server"
imap.capable?(:starttls)  or raise "Cannot start TLS"
imap.starttls

if imap.auth_capable?("PLAIN")
  imap.authenticate "PLAIN", username, password
elsif !imap.capability?("LOGINDISABLED")
  imap.login username, password
else
  raise "No acceptable authentication mechanisms"
end

# Support for "UTF8=ACCEPT" implies support for "ENABLE"
imap.enable :utf8 if imap.capable?("UTF8=ACCEPT")

namespaces  = imap.namespace if imap.capable?(:namespace)
mbox_prefix = namespaces&.personal&.first&.prefix || ""
mbox_delim  = namespaces&.personal&.first&.delim  || "/"
mbox_path   = prefix + %w[path to my mailbox].join(delim)
imap.create mbox_path

基本的 IMAP4rev1 功能

IMAP4rev1 服务器必须在其功能列表中通告 IMAP4rev1。IMAP4rev1 服务器必须实现 STARTTLSAUTH=PLAINLOGINDISABLED 功能。有关这些功能的含义,请参阅 starttlsloginauthenticate

缓存 CAPABILITY 响应

Net::IMAP 会根据 IMAP4rev2 §6.1.1§6.2§7.1 中的要求和建议,自动存储和丢弃功能数据。请使用 capable?auth_capable?capabilities 来使用此缓存并避免不必要地发送 capability 命令。

服务器可以使用 PREAUTHOK greeting 中的 CAPABILITY ResponseCode 通告其初始功能。当 TLS 启动 (starttls) 且完成身份验证 (loginauthenticate) 后,服务器的功能可能会更改,并且缓存的功能将被丢弃。服务器可以使用 loginauthenticateOK TaggedResponse 发送更新的功能,这些功能将由 Net::IMAP 缓存。但是,必须忽略 starttlsTaggedResponse,它在 TLS 启动之前发送并且不受保护。

将功能值存储到变量时,请注意它们是否已正确丢弃或重置,尤其是在 starttls 之后。

使用 IMAP4rev1 扩展

有关所有标准功能及其参考 RFC 的列表,请参阅 IANA IMAP4 功能注册表

IMAP4rev1 服务器在客户端显式调用某个功能(例如,发送特定于该功能的命令或命令参数)之前,不得激活与基本规范不兼容的行为。服务器可以在任何时候发送具有向后兼容行为的数据,例如响应代码或邮箱属性,而无需客户端操作。

调用 Net::IMAP 未知的功能可能会导致意外的行为和错误。例如,当收到未知的响应语法时,会引发 ResponseParseError。调用服务器不支持的命令或命令参数可能会引发 NoResponseErrorBadResponseError 或导致其他意外行为。

某些功能必须使用 enable 命令显式激活。有关详细信息,请参见 enable

线程安全

Net::IMAP 支持并发线程。例如:

imap = Net::IMAP.new("imap.foo.net", "imap2")
imap.authenticate("scram-md5", "bar", "password")
imap.select("inbox")
fetch_thread = Thread.start { imap.fetch(1..-1, "UID") }
search_result = imap.search(["BODY", "hello"])
fetch_result = fetch_thread.value
imap.disconnect

此脚本并发调用 FETCH 命令和 SEARCH 命令。

错误

IMAP 服务器可以发送三种不同类型的响应来指示失败:

NO

尝试的命令无法成功完成。例如,用于登录的用户名/密码不正确;所选的邮箱不存在;等等。

BAD

客户端的请求不符合服务器对 IMAP 协议的理解。这包括在错误的客户端状态下尝试命令;例如,在没有 SELECT 当前邮箱的情况下尝试执行 SEARCH 命令。它还可以表示发生了内部服务器故障(例如磁盘崩溃)。

BYE

服务器正在说再见。这可能是正常注销序列的一部分,也可以作为登录序列的一部分,表示服务器(由于某种原因)不愿意接受您的连接。作为对任何其他命令的响应,它表示服务器正在关闭,或者服务器由于不活动而使客户端连接超时。

这三种错误响应分别由错误 Net::IMAP::NoResponseErrorNet::IMAP::BadResponseErrorNet::IMAP::ByeResponseError 表示,它们都是 Net::IMAP::ResponseError 的子类。本质上,所有涉及向服务器发送请求的方法都可能产生这些错误之一。下面只记录了最相关的实例。

由于 IMAP 类使用套接字进行通信,因此其方法也容易受到在使用套接字时可能发生的各种错误的影响。这些错误通常表示为 Errno 错误。例如,任何涉及向服务器发送请求和/或接收服务器响应的方法,如果网络连接意外中断,都可能引发 Errno::EPIPE 错误。请参见 socket(7)、ip(7)、tcp(7)、socket(2)、connect(2) 和相关的 man 页面。

最后,如果发现底层数据格式不正确(例如,在 UTF-8 和 UTF-16 之间转换时),则会抛出 Net::IMAP::DataFormatError;如果服务器响应不可解析,则会抛出 Net::IMAP::ResponseParseError

这里有什么?

连接控制方法

  • Net::IMAP.new:创建一个新的 IMAP 客户端,该客户端会立即连接并等待服务器成功问候,然后该方法返回。

  • starttls:要求服务器将明文连接升级为使用 TLS。

  • logout:告诉服务器结束会话。进入“注销”状态。

  • disconnect:断开连接(不首先发送 logout)。

  • disconnected?:如果连接已关闭,则为 True。

服务器功能

处理服务器响应

核心 IMAP 命令

以下命令由 [IMAP4rev1] 基本规范或以下扩展之一定义:[IDLE]、[NAMESPACE]、[UNSELECT]、[ENABLE]、[MOVE]。这些扩展被现代 IMAP4rev1 服务器广泛支持,并且都已集成到 [IMAP4rev2] 中。注意: Net::IMAP 尚未支持 IMAP4rev2。

任何状态

  • capability:以字符串数组的形式返回服务器的功能。

    通常,应使用 #capable? 而不是显式向服务器发送 CAPABILITY 命令。

  • noop:允许服务器发送未经请求的未标记 responses

  • logout:告诉服务器结束会话。进入“注销”状态。

未认证状态

除了任何状态的命令之外,以下命令在“未认证”状态下有效:

  • starttls:将明文连接升级为使用 TLS。

    需要 STARTTLS 功能。

  • authenticate:使用给定的 SASL 机制 和凭据向服务器标识客户端。进入“已认证”状态。

    服务器应列出受支持机制的 "AUTH=#{mechanism}" 功能。

  • login:使用纯文本密码向服务器标识客户端。通常首选使用 authenticate。进入“已认证”状态。

    不得列出 LOGINDISABLED 功能。

已认证状态

除了任何状态的命令之外,以下命令在“已认证”状态下有效:

  • enable:启用向后不兼容的服务器扩展。需要 ENABLEIMAP4rev2 功能。

  • select:打开邮箱并进入“已选择”状态。

  • examine:以只读方式打开邮箱,并进入“已选择”状态。

  • create:创建一个新的邮箱。

  • delete:永久删除一个邮箱。

  • rename:更改邮箱的名称。

  • subscribe:将邮箱添加到“已订阅”集合中。

  • unsubscribe:从“已订阅”集合中删除邮箱。

  • list:返回与给定模式匹配的邮箱的名称和属性。

  • namespace:返回邮箱命名空间,带有路径前缀和分隔符。需要 NAMESPACEIMAP4rev2 功能。

  • status:返回邮箱信息,例如消息计数、未读消息计数、UIDVALIDITYUIDNEXT

  • append:将消息附加到邮箱的末尾。

  • idle:允许服务器向客户端发送更新,而无需客户端使用 noop 进行轮询。需要 IDLEIMAP4rev2 功能。

  • 已过时 lsub已由 LIST-EXTENDED 替换,并从 IMAP4rev2 中删除。 列出“已订阅”集合中的邮箱。

    注意: Net::IMAP 尚未实现 LIST-EXTENDED

已选择状态

除了任何状态的命令和“已认证”命令之外,以下命令在“已选择”状态下有效:

  • close:关闭邮箱并返回“已认证”状态,删除已删除的消息,除非邮箱以只读方式打开。

  • unselect:关闭邮箱并返回“已认证”状态,而不删除任何消息。需要 UNSELECTIMAP4rev2 功能。

  • expunge:永久删除设置了 Deleted 标志的消息。

  • uid_expunge:限制删除操作仅删除指定的 UID。需要 UIDPLUSIMAP4rev2 功能。

  • searchuid_search:返回与给定搜索条件匹配的消息的序列号或 UID。

  • fetchuid_fetch:返回与一组消息关联的数据,这些消息由序列号或 UID 指定。

  • storeuid_store:更改消息的标志。

  • copyuid_copy:将指定的消息复制到指定目标邮箱的末尾。

  • moveuid_move:将指定的消息移动到指定目标邮箱的末尾,并从当前邮箱中删除它们。需要 MOVEIMAP4rev2 功能。

  • check已过时: 已从 IMAP4rev2 中删除。 可以使用 noopidle 替换。

注销状态

在“注销”状态下,任何 IMAP 命令均无效。如果套接字仍然打开,Net::IMAP 将在收到服务器确认后关闭它。已启动并正在等待响应的 IMAP 命令,以及注销后调用的任何命令都会引发异常。

IMAP 扩展支持

RFC9051: IMAP4rev2

尽管 IMAP4rev2 尚未完全支持,但 Net::IMAP 支持已纳入其中的几个扩展:ENABLEIDLEMOVENAMESPACESASL-IRUIDPLUSUNSELECTSTATUS=SIZE 以及 BINARY 的提取端。这些扩展的命令与上面的 核心 IMAP 命令一起列出。

以下扩展已纳入 IMAP4rev2,但目前 Net::IMAP 不支持或不完全支持:RFC4466 扩展、SEARCHRESLIST-EXTENDEDLIST-STATUSLITERAL-SPECIAL-USE

RFC2087: QUOTA

  • getquota:返回配额根的资源使用情况和限制

  • getquotaroot:返回邮箱的配额根列表,以及它们的资源使用情况和限制。

  • setquota:设置给定配额根的资源限制。

RFC2177: IDLE

已纳入 IMAP4rev2,并且也包含在上面的 核心 IMAP 命令中。

  • idle:允许服务器向客户端发送更新,而无需客户端使用 noop 进行轮询。

RFC2342: NAMESPACE

已纳入 IMAP4rev2,并且也包含在上面的 核心 IMAP 命令中。

  • namespace:返回带有路径前缀和分隔符的邮箱命名空间。

RFC2971: ID

  • id:交换客户端和服务器的实现信息。

RFC3516: BINARY

BINARY 的提取端已纳入 IMAP4rev2

注意: append 命令的二进制扩展目前受支持。

RFC3691: UNSELECT

已纳入 IMAP4rev2,并且也包含在上面的 核心 IMAP 命令中。

  • unselect:关闭邮箱并返回到“已验证”状态,而不清除任何邮件。

RFC4314: ACL

  • getacl:列出经过身份验证的用户对邮箱的访问权限。

  • setacl:设置用户对邮箱的访问权限

注意: 目前不支持 DELETEACLLISTRIGHTSMYRIGHTS

RFC4315: UIDPLUS

已纳入 IMAP4rev2,并且也包含在上面的 核心 IMAP 命令中。

RFC4731: ESEARCH

已纳入 IMAP4rev2

RFC4959: SASL-IR

已纳入 IMAP4rev2

RFC5161: ENABLE

已纳入 IMAP4rev2,并且也包含在上面的 核心 IMAP 命令中。

  • enable:启用向后不兼容的服务器扩展。

RFC5256: SORT

RFC5256: THREAD

X-GM-EXT-1

X-GM-EXT-1 是一个非标准的 Gmail 扩展。请参阅 Google 的文档

  • 使用对 X-GM-MSGID(唯一消息 ID)、X-GM-THRID(线程 ID)和 X-GM-LABELS(Gmail 标签)的支持更新 fetchuid_fetch

  • 使用 X-GM-RAW 搜索属性更新 search

  • xlist:由 list 响应中的 SPECIAL-USE 属性取代。

注意: OBJECTID 扩展应该替换 X-GM-MSGIDX-GM-THRID,但 Gmail 不支持它(截至 2023-11-10)。

RFC6851: MOVE

已纳入 IMAP4rev2,并且也包含在上面的 核心 IMAP 命令中。

  • moveuid_move:将指定的消息移动到指定的目标邮箱的末尾,并从当前邮箱中清除它们。

RFC6855: UTF8=ACCEPT, UTF8=ONLY

  • 有关支持 UTF-8 字符串编码的信息,请参阅 enable

RFC7162: CONDSTORE

RFC8438: STATUS=SIZE

  • 使用 SIZE 状态属性更新 status

RFC8474: OBJECTID

RFC9394: PARTIAL

参考资料

[IMAP4rev1]

Crispin, M., “INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1”, RFC 3501, DOI 10.17487/RFC3501, 2003 年 3 月, <www.rfc-editor.org/info/rfc3501>.

[IMAP-ABNF-EXT]

Melnikov, A. 和 C. Daboo, “Collected Extensions to IMAP4 ABNF”, RFC 4466, DOI 10.17487/RFC4466, 2006 年 4 月, <www.rfc-editor.org/info/rfc4466>.

注意:Net::IMAP 尚无法解析整个 RFC4466 语法。

[IMAP4rev2]

Melnikov, A., Ed. 和 B. Leiba, Ed., “Internet Message Access Protocol (IMAP) - Version 4rev2”, RFC 9051, DOI 10.17487/RFC9051, 2021 年 8 月, <www.rfc-editor.org/info/rfc9051>.

注意:Net::IMAP 尚未完全兼容 IMAP4rev2。

[IMAP-IMPLEMENTATION]

Leiba, B., “IMAP4 Implementation Recommendations”, RFC 2683, DOI 10.17487/RFC2683, 1999 年 9 月, <www.rfc-editor.org/info/rfc2683>.

[IMAP-MULTIACCESS]

Gahrns, M., “IMAP4 Multi-Accessed Mailbox Practice”, RFC 2180, DOI 10.17487/RFC2180, 1997 年 7 月, <www.rfc-editor.org/info/rfc2180>.

[UTF7]

Goldsmith, D. 和 M. Davis, “UTF-7 A Mail-Safe Transformation Format of Unicode”, RFC 2152, DOI 10.17487/RFC2152, 1997 年 5 月, <www.rfc-editor.org/info/rfc2152>.

消息信封和正文结构

[RFC5322]

Resnick, P., Ed., “Internet Message Format”, RFC 5322, DOI 10.17487/RFC5322, 2008 年 10 月, <www.rfc-editor.org/info/rfc5322>.

注意:已过时 RFC-2822 (2001 年 4 月) 和 RFC-822 (1982 年 8 月)。

[CHARSET]

Freed, N. 和 J. Postel,“IANA 字符集注册程序”,BCP 19,RFC 2978,DOI 10.17487/RFC2978,2000 年 10 月,<www.rfc-editor.org/info/rfc2978>。

[DISPOSITION]

Troost, R.,Dorner, S. 和 K. Moore, Ed.,“在 Internet 消息中传递表示信息:Content-Disposition 头部字段”,RFC 2183,DOI 10.17487/RFC2183,1997 年 8 月,<www.rfc-editor.org/info/rfc2183>。

[MIME-IMB]

Freed, N. 和 N. Borenstein,“多用途 Internet 邮件扩展 (MIME) 第一部分:Internet 消息主体的格式”,RFC 2045,DOI 10.17487/RFC2045,1996 年 11 月,<www.rfc-editor.org/info/rfc2045>。

[MIME-IMT]

Freed, N. 和 N. Borenstein,“多用途 Internet 邮件扩展 (MIME) 第二部分:媒体类型”,RFC 2046,DOI 10.17487/RFC2046,1996 年 11 月,<www.rfc-editor.org/info/rfc2046>。

[MIME-HDRS]

Moore, K.,“MIME(多用途 Internet 邮件扩展)第三部分:非 ASCII 文本的消息头扩展”,RFC 2047,DOI 10.17487/RFC2047,1996 年 11 月,<www.rfc-editor.org/info/rfc2047>。

[RFC2231]

Freed, N. 和 K. Moore,“MIME 参数值和编码词扩展:字符集、语言和延续”,RFC 2231,DOI 10.17487/RFC2231,1997 年 11 月,<www.rfc-editor.org/info/rfc2231>。

[I18n-HDRS]

Yang, A., Steele, S. 和 N. Freed,“国际化电子邮件头”,RFC 6532,DOI 10.17487/RFC6532,2012 年 2 月,<www.rfc-editor.org/info/rfc6532>。

[LANGUAGE-TAGS]

Alvestrand, H.,“内容语言头”,RFC 3282,DOI 10.17487/RFC3282,2002 年 5 月,<www.rfc-editor.org/info/rfc3282>。

[LOCATION]

Palme, J., Hopmann, A. 和 N. Shelness,“聚合文档(例如 HTML (MHTML))的 MIME 封装”,RFC 2557,DOI 10.17487/RFC2557,1999 年 3 月,<www.rfc-editor.org/info/rfc2557>。

[MD5]

Myers, J. 和 M. Rose,“Content-MD5 头部字段”,RFC 1864,DOI 10.17487/RFC1864,1995 年 10 月,<www.rfc-editor.org/info/rfc1864>。

[RFC3503]

Melnikov, A.,“Internet 消息访问协议(IMAP)的消息处理通知 (MDN) 配置文件”,RFC 3503,DOI 10.17487/RFC3503,2003 年 3 月,<www.rfc-editor.org/info/rfc3503>。

IMAP 扩展

[QUOTA]

Melnikov, A.,“IMAP QUOTA 扩展”,RFC 9208,DOI 10.17487/RFC9208,2022 年 3 月,<www.rfc-editor.org/info/rfc9208>。

注意:取代 RFC-2087(1997 年 1 月)Net::IMAP 尚未完全支持 RFC9208 的更新。

[IDLE]

Leiba, B.,“IMAP4 IDLE 命令”,RFC 2177,DOI 10.17487/RFC2177,1997 年 6 月,<www.rfc-editor.org/info/rfc2177>。

[NAMESPACE]

Gahrns, M. 和 C. Newman,“IMAP4 命名空间”,RFC 2342,DOI 10.17487/RFC2342,1998 年 5 月,<www.rfc-editor.org/info/rfc2342>。

[ID]

Showalter, T.,“IMAP4 ID 扩展”,RFC 2971,DOI 10.17487/RFC2971,2000 年 10 月,<www.rfc-editor.org/info/rfc2971>。

[BINARY]

Nerenberg, L.,“IMAP4 二进制内容扩展”,RFC 3516,DOI 10.17487/RFC3516,2003 年 4 月,<www.rfc-editor.org/info/rfc3516>。

[ACL]

Melnikov, A.,“IMAP4 访问控制列表 (ACL) 扩展”,RFC 4314,DOI 10.17487/RFC4314,2005 年 12 月,<www.rfc-editor.org/info/rfc4314>。

[UIDPLUS]

Crispin, M.,“Internet 消息访问协议 (IMAP) - UIDPLUS 扩展”,RFC 4315,DOI 10.17487/RFC4315,2005 年 12 月,<www.rfc-editor.org/info/rfc4315>。

[SORT]

Crispin, M. 和 K. Murchison,“Internet 消息访问协议 - SORT 和 THREAD 扩展”,RFC 5256,DOI 10.17487/RFC5256,2008 年 6 月,<www.rfc-editor.org/info/rfc5256>。

[THREAD]

Crispin, M. 和 K. Murchison,“Internet 消息访问协议 - SORT 和 THREAD 扩展”,RFC 5256,DOI 10.17487/RFC5256,2008 年 6 月,<www.rfc-editor.org/info/rfc5256>。

[RFC5530]

Gulbrandsen, A.,“IMAP 响应代码”,RFC 5530,DOI 10.17487/RFC5530,2009 年 5 月,<www.rfc-editor.org/info/rfc5530>。

[MOVE]

Gulbrandsen, A. 和 N. Freed, Ed.,“Internet 消息访问协议 (IMAP) - MOVE 扩展”,RFC 6851,DOI 10.17487/RFC6851,2013 年 1 月,<www.rfc-editor.org/info/rfc6851>。

[UTF8=ACCEPT]
[UTF8=ONLY]

Resnick, P., Ed., Newman, C., Ed. 和 S. Shen, Ed.,“IMAP 对 UTF-8 的支持”,RFC 6855,DOI 10.17487/RFC6855,2013 年 3 月,<www.rfc-editor.org/info/rfc6855>。

[CONDSTORE]
[QRESYNC]

Melnikov, A. 和 D. Cridland,“IMAP 扩展:快速标志更改重新同步 (CONDSTORE) 和快速邮箱重新同步 (QRESYNC)”,RFC 7162,DOI 10.17487/RFC7162,2014 年 5 月,<www.rfc-editor.org/info/rfc7162>。

[OBJECTID]

Gondwana, B., Ed.,“对象标识符的 IMAP 扩展”,RFC 8474,DOI 10.17487/RFC8474,2018 年 9 月,<www.rfc-editor.org/info/rfc8474>。

[PARTIAL]

Melnikov, A.,Achuthan, A.,Nagulakonda, V. 和 L. Alves,“用于分页搜索和获取的 IMAP PARTIAL 扩展”,RFC 9394,DOI 10.17487/RFC9394,2023 年 6 月,<www.rfc-editor.org/info/rfc9394>。

IANA 注册表

对于当前不支持的功能:

常量

数据
ENABLE_ALIASES

支持的功能的别名,用于 enable 命令。

RESPONSES_DEPRECATION_MSG
RETURN_START
RETURN_WHOLE
STRFDATE

IMAP4 date 的 strftime/strptime 格式,不包括可选的双引号。通过 encode_datedecode_date 方法使用。

date            = date-text / DQUOTE date-text DQUOTE
date-text       = date-day "-" date-month "-" date-year

date-day        = 1*2DIGIT
                    ; Day of month
date-month      = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
                  "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
date-year       = 4DIGIT
STRFTIME

IMAP4 date-time 的 strftime/strptime 格式,包括双引号。请参阅 encode_datetimedecode_datetime 方法。

date-time       = DQUOTE date-day-fixed "-" date-month "-" date-year
                  SP time SP zone DQUOTE

date-day-fixed  = (SP DIGIT) / 2DIGIT
                    ; Fixed-format version of date-day
date-month      = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
                  "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
date-year       = 4DIGIT
time            = 2DIGIT ":" 2DIGIT ":" 2DIGIT
                    ; Hours minutes seconds
zone            = ("+" / "-") 4DIGIT
                    ; Signed four-digit value of hhmm representing
                    ; hours and minutes east of Greenwich (that is,
                    ; the amount that the given time differs from
                    ; Universal Time).  Subtracting the timezone
                    ; from the given time will give the UT form.
                    ; The Universal Time zone is "+0000".

请注意,Time.strptime "%d" 灵活地解析空格或零填充。但是,双引号是不是可选的。

VERSION

属性

config[R]

客户端配置。请参阅 Net::IMAP::Config

默认情况下,客户端的本地配置继承自全局 Net::IMAP.config

greeting[R]

返回服务器的初始问候语,即 UntaggedResponse

host[R]

此客户端连接到的主机名

port[R]

此客户端连接到的端口

ssl_ctx[R]

返回 TLS 尝试时 SSLSocket 使用的 SSLContext,即使 TLS 握手不成功。上下文对象将被冻结。

对于明文连接,返回 nil

ssl_ctx_params[R]

返回发送到 ssl_ctx set_params 的参数,当连接尝试使用 TLS 时(即使不成功)。

对于明文连接,返回 false

公共类方法

config() 点击以切换源

返回全局 Config 对象

# File net-imap-0.5.4/lib/net/imap.rb, line 755
def self.config; Config.global end
debug() 点击以切换源

返回全局调试模式。

# File net-imap-0.5.4/lib/net/imap.rb, line 758
def self.debug; config.debug end
debug=(val) 点击以切换源

设置全局调试模式。

# File net-imap-0.5.4/lib/net/imap.rb, line 761
def self.debug=(val)
  config.debug = val
end
decode_date(string) → Date 点击以切换源

string 解码为 IMAP 格式的“date”。

双引号是可选的。月份中的日期可以用零或空格填充。请参阅 STRFDATE

# File net-imap-0.5.4/lib/net/imap/data_encoding.rb, line 90
def self.decode_date(string)
  string = string.delete_prefix('"').delete_suffix('"')
  Date.strptime(string, STRFDATE)
end
也别名为:parse_date
decode_datetime(string) → DateTime 点击以切换源

string 解码为 IMAP4 格式的“date-time”。

注意:尽管双引号在 IMAP 语法中不是可选的,但 Net::IMAP 当前将“date-time”值解析为“带引号”的字符串,这将删除引号。为了对已解析为带引号字符串的字符串有用,此方法使双引号成为可选的。

请参阅 STRFTIME

# File net-imap-0.5.4/lib/net/imap/data_encoding.rb, line 112
def self.decode_datetime(string)
  unless string.start_with?(?") && string.end_with?(?")
    string = '"%s"' % [string]
  end
  DateTime.strptime(string, STRFTIME)
end
也别名为:parse_datetime
decode_time(string) → Time 点击以切换源

string 解码为 IMAP4 格式的“date-time”。

decode_datetime 相同,但返回 Time 而不是 DateTime。

# File net-imap-0.5.4/lib/net/imap/data_encoding.rb, line 124
def self.decode_time(string)
  unless string.start_with?(?") && string.end_with?(?")
    string = '"%s"' % [string]
  end
  Time.strptime(string, STRFTIME)
end
也别名为:parse_time
decode_utf7(s) 点击切换源代码

将字符串从修改后的 UTF-7 格式解码为 UTF-8 格式。

UTF-7 是一种 7 位 Unicode 编码 [UTF7]。IMAP 使用其稍作修改的版本来编码包含非 ASCII 字符的邮箱名称;请参阅 [IMAP] 第 5.1.3 节。

Net::IMAP 不会自动将邮箱名称编码和解码为 UTF-7 或从 UTF-7 解码。

# File net-imap-0.5.4/lib/net/imap/data_encoding.rb, line 57
def self.decode_utf7(s)
  return s.gsub(/&([A-Za-z0-9+,]+)?-/n) {
    if base64 = $1
      (base64.tr(",", "/") + "===").unpack1("m").encode(Encoding::UTF_8, Encoding::UTF_16BE)
    else
      "&"
    end
  }
end
default_imap_port()
别名为:default_port
default_imaps_port()
别名为:default_tls_port
default_port() 点击切换源代码

IMAP 连接的默认端口,端口号为 143

# File net-imap-0.5.4/lib/net/imap.rb, line 766
def self.default_port
  return PORT
end
也别名为:default_imap_port
default_ssl_port()
别名为:default_tls_port
default_tls_port() 点击切换源代码

IMAPS 连接的默认端口,端口号为 993

# File net-imap-0.5.4/lib/net/imap.rb, line 771
def self.default_tls_port
  return SSL_PORT
end
encode_date(date) 点击切换源代码

time 格式化为 IMAP4 日期。

# File net-imap-0.5.4/lib/net/imap/data_encoding.rb, line 80
def self.encode_date(date)
  date.to_date.strftime STRFDATE
end
也别名为:format_date
encode_datetime(time) → string 点击切换源代码

time 格式化为 IMAP4 日期时间。

# File net-imap-0.5.4/lib/net/imap/data_encoding.rb, line 98
def self.encode_datetime(time)
  time.to_datetime.strftime STRFTIME
end
也别名为:encode_time
encode_time(time)
也别名为:format_time
别名为:encode_datetime
encode_utf7(s) 点击切换源代码

将字符串从 UTF-8 格式编码为修改后的 UTF-7 格式。

# File net-imap-0.5.4/lib/net/imap/data_encoding.rb, line 68
def self.encode_utf7(s)
  return s.gsub(/(&)|[^\x20-\x7e]+/) {
    if $1
      "&-"
    else
      base64 = [$&.encode(Encoding::UTF_16BE)].pack("m0")
      "&" + base64.delete("=").tr("/", ",") + "-"
    end
  }.force_encoding("ASCII-8BIT")
end
format_date(date)
别名为:encode_date
format_datetime(time) 点击切换源代码
已弃用

原始版本返回格式不正确的字符串。由 encode_datetimeformat_time 返回的字符串使用 “date-time” 的正确 IMAP4rev1 语法。

为向后兼容,暂时保留了这种无效格式。未来版本将更改此方法以返回正确的格式。

# File net-imap-0.5.4/lib/net/imap/data_encoding.rb, line 149
def self.format_datetime(time)
  warn("#{self}.format_datetime incorrectly formats IMAP date-time. " \
       "Convert to #{self}.encode_datetime or #{self}.format_time instead.",
       uplevel: 1, category: :deprecated)
  time.strftime("%d-%b-%Y %H:%M %z")
end
format_time(time)
别名为:encode_time
new(host, port: nil, ssl: nil, config: Config.global, **config_options) 点击切换源代码

创建一个新的 Net::IMAP 对象,并将其连接到指定的 host

选项

接受以下选项

port

端口号。当 ssl 为真值时,默认为 993,否则为 143。

ssl

如果为 true,则连接将使用 TLS,并使用 OpenSSL::SSL::SSLContext#set_params 设置的默认参数。如果 ssl 是一个哈希,则会将其传递给 OpenSSL::SSL::SSLContext#set_params;键是 SSLContext 上的属性赋值方法的名称。例如

ca_file

包含 PEM 格式 CA 证书的文件的路径。

ca_path

包含 PEM 格式 CA 证书的目录的路径。

min_version

设置支持的 SSL/TLS 协议版本的下限。设置为 OpenSSL 常量,例如 OpenSSL::SSL::TLS1_2_VERSION

verify_mode

SSL 会话验证模式。有效的模式包括 OpenSSL::SSL::VERIFY_PEEROpenSSL::SSL::VERIFY_NONE

有关其他有效的 SSL 上下文参数,请参阅 OpenSSL::SSL::SSLContext

有关已弃用的 SSL 参数,请参阅 DeprecatedClientOptions.new

config

一个 Net::IMAP::Config 对象,用作 config 的基础。默认情况下,使用全局 Net::IMAP.config

注意:config 不会直接设置 config,而是设置用于继承的父级配置。每个客户端都会创建自己唯一的 config

所有其他关键字参数都会转发给 Net::IMAP::Config.new,以初始化客户端的 config。例如

open_timeout

等待连接打开的秒数

idle_response_timeout

等待接收 IDLE 响应的秒数

有关其他有效选项,请参阅 Net::IMAP::Config

示例

连接到 mail.example.com 上的明文端口 143 并接收服务器问候语

imap = Net::IMAP.new('mail.example.com', ssl: false) # => #<Net::IMAP:0x00007f79b0872bd0>
imap.port          => 143
imap.tls_verified? => false
imap.greeting      => name: ("OK" | "PREAUTH") => status
status # => "OK"
# The client is connected in the "Not Authenticated" state.

使用 TLS 连接到端口 993

imap = Net::IMAP.new('mail.example.com', ssl: true) # => #<Net::IMAP:0x00007f79b0872bd0>
imap.port          => 993
imap.tls_verified? => true
imap.greeting      => name: (/OK/i | /PREAUTH/i) => status
case status
in /OK/i
  # The client is connected in the "Not Authenticated" state.
  imap.authenticate("PLAIN", "joe_user", "joes_password")
in /PREAUTH/i
  # The client is connected in the "Authenticated" state.
end

使用先前的身份验证进行连接,例如使用 SSL 证书

ssl_ctx_params = {
  cert: OpenSSL::X509::Certificate.new(File.read("client.crt")),
  key:  OpenSSL::PKey::EC.new(File.read('client.key')),
  extra_chain_cert: [
    OpenSSL::X509::Certificate.new(File.read("intermediate.crt")),
  ],
}
imap = Net::IMAP.new('mail.example.com', ssl: ssl_ctx_params)
imap.port          => 993
imap.tls_verified? => true
imap.greeting      => name: "PREAUTH"
# The client is connected in the "Authenticated" state.

异常

最常见的错误是

Errno::ECONNREFUSED

连接被 host 或中间防火墙拒绝。

Errno::ETIMEDOUT

连接超时(可能是由于中间防火墙丢弃了数据包)。

Errno::ENETUNREACH

没有到达该网络的路由。

SocketError

主机名未知或其他套接字错误。

Net::IMAP::ByeResponseError

已成功连接到主机,但主机立即道别。

调用超类方法
# File net-imap-0.5.4/lib/net/imap.rb, line 923
def initialize(host, port: nil, ssl:  nil,
               config: Config.global, **config_options)
  super()
  # Config options
  @host = host
  @config = Config.new(config, **config_options)
  @port = port || (ssl ? SSL_PORT : PORT)
  @ssl_ctx_params, @ssl_ctx = build_ssl_ctx(ssl)

  # Basic Client State
  @utf8_strings = false
  @debug_output_bol = true
  @exception = nil
  @greeting = nil
  @capabilities = nil

  # Client Protocol Receiver
  @parser = ResponseParser.new(config: @config)
  @responses = Hash.new {|h, k| h[k] = [] }
  @response_handlers = []
  @receiver_thread = nil
  @receiver_thread_exception = nil
  @receiver_thread_terminating = false

  # Client Protocol Sender (including state for currently running commands)
  @tag_prefix = "RUBY"
  @tagno = 0
  @tagged_responses = {}
  @tagged_response_arrival = new_cond
  @continued_command_tag = nil
  @continuation_request_arrival = new_cond
  @continuation_request_exception = nil
  @idle_done_cond = nil
  @logout_command_tag = nil

  # Connection
  @tls_verified = false
  @sock = tcp_socket(@host, @port)
  start_tls_session if ssl_ctx
  start_imap_connection
end
parse_date(string)
别名为:decode_date
parse_datetime(string)
别名为:decode_datetime
parse_time(string)
别名为:decode_time

私有类方法

saslprep(string, **opts) 点击切换源代码

委托给 Net::IMAP::StringPrep::SASLprep#saslprep

# File net-imap-0.5.4/lib/net/imap.rb, line 3572
def self.saslprep(string, **opts)
  Net::IMAP::StringPrep::SASLprep.saslprep(string, **opts)
end

公共实例方法

add_response_handler(handler = nil, &block) 点击切换源代码

添加响应处理程序。例如,要检测服务器何时发送新的 EXISTS 响应(通常表示向邮箱添加了新消息),请在选择邮箱后添加以下处理程序

imap.add_response_handler { |resp|
  if resp.kind_of?(Net::IMAP::UntaggedResponse) and resp.name == "EXISTS"
    puts "Mailbox now has #{resp.data} messages"
  end
}

相关:remove_response_handlerresponse_handlers

# File net-imap-0.5.4/lib/net/imap.rb, line 3081
def add_response_handler(handler = nil, &block)
  raise ArgumentError, "two Procs are passed" if handler && block
  synchronize do
    @response_handlers.push(block || handler)
  end
end
append(mailbox, message, flags = nil, date_time = nil) 点击切换源代码

发送 APPEND 命令 [IMAP4rev1 §6.3.11] 以将 message 追加到 mailbox 的末尾。可选的 flags 参数是初始传递给新消息的标志数组。可选的 date_time 参数指定要分配给新消息的创建时间;它默认为当前时间。

例如

imap.append("inbox", <<EOF.gsub(/\n/, "\r\n"), [:Seen], Time.now)
Subject: hello
From: [email protected]
To: [email protected]

hello world
EOF

如果邮箱不存在(它不会自动创建),或者如果 flags、date_time 或 message 参数包含错误,则会引发 Net::IMAP::NoResponseError

功能

如果支持 UIDPLUS [RFC4315] 并且目标支持持久 UID,则服务器的响应应包含带有 UIDPlusDataAPPENDUID 响应代码。这将报告目标邮箱的 UIDVALIDITY 和追加消息的已分配 UID。

# File net-imap-0.5.4/lib/net/imap.rb, line 1856
def append(mailbox, message, flags = nil, date_time = nil)
  args = []
  if flags
    args.push(flags)
  end
  args.push(date_time) if date_time
  args.push(Literal.new(message))
  send_command("APPEND", mailbox, *args)
end
auth_capable?(mechanism) 点击切换源代码

返回服务器是否支持给定的 SASL mechanism 用于 authenticate 命令。当 capabilities 包含 "AUTH=#{mechanism.to_s.upcase}" 时,支持 mechanism。如果可用,将使用缓存的功能,而无需向服务器发送新的 capability 命令。

imap.capable?      "AUTH=PLAIN"  # => true
imap.auth_capable? "PLAIN"       # => true
imap.auth_capable? "blurdybloop" # => false

相关:authenticateauth_mechanismscapable?capabilities

# File net-imap-0.5.4/lib/net/imap.rb, line 1067
def auth_capable?(mechanism)
  capable? "AUTH=#{mechanism}"
end
auth_mechanisms() 点击切换源代码

返回服务器声明支持的 authenticate 机制。这些机制是从带有 AUTH= 前缀的 capabilities 中派生出来的。

当连接是明文还是使用 TLS 时,这可能会有所不同。大多数服务器会在连接经过身份验证后从 capabilities 中删除所有 AUTH= 机制。

imap = Net::IMAP.new(hostname, ssl: false)
imap.capabilities    # => ["IMAP4REV1", "LOGINDISABLED"]
imap.auth_mechanisms # => []

imap.starttls
imap.capabilities    # => ["IMAP4REV1", "AUTH=PLAIN", "AUTH=XOAUTH2",
                     #     "AUTH=OAUTHBEARER"]
imap.auth_mechanisms # => ["PLAIN", "XOAUTH2", "OAUTHBEARER"]

imap.authenticate("XOAUTH2", username, oauth2_access_token)
imap.auth_mechanisms # => []

相关:authenticateauth_capable?capabilities

# File net-imap-0.5.4/lib/net/imap.rb, line 1050
def auth_mechanisms
  capabilities
    .grep(/\AAUTH=/i)
    .map { _1.delete_prefix("AUTH=") }
end
authenticate(mechanism, *, sasl_ir: config.sasl_ir, registry: Net::IMAP::SASL.authenticators, **, &) → ok_resp 点击切换源代码

发送 AUTHENTICATE 命令 [IMAP4rev1 §6.2.2] 以对客户端进行身份验证。如果成功,连接将进入 “已身份验证” 状态。

mechanism 是要使用的 SASL 身份验证机制的名称。

sasl_ir 允许或禁止发送 “初始响应”(请参阅下面的 SASL-IR 功能)。默认为 configsasl_ir 的值,默认为 true

registry kwarg 可用于从自定义注册表中选择机制实现。请参阅 SASL.authenticatorSASL::Authenticators

所有其他参数都会转发到请求的机制的已注册 SASL 身份验证器。必须查阅每种机制的文档以了解其特定参数。

相关:loginstarttlsauth_capable?auth_mechanisms

机制

每个机制都有不同的属性和要求。请查阅您正在使用的特定机制的文档。

ANONYMOUS (匿名)

请参阅 AnonymousAuthenticator

允许用户在不进行身份验证或不公开身份的情况下访问公共服务或资源。

EXTERNAL (外部)

请参阅 ExternalAuthenticator

使用已建立的凭据(如 TLS 证书或 IPsec)进行身份验证。

OAUTHBEARER

请参阅 OAuthBearerAuthenticator

使用 OAuth2 Bearer 令牌登录。这是将 OAuth2 与 SASL 一起使用的标准机制,但尚未像 XOAUTH2 那样广泛部署。

PLAIN (明文)

请参阅 PlainAuthenticator

使用明文用户名和密码登录。

SCRAM-SHA-1
SCRAM-SHA-256

请参阅 ScramAuthenticator

通过用户名和密码登录。密码不会发送到服务器,而是用于加盐质询/响应交换。 Net::IMAP::SASL 直接支持 SCRAM-SHA-1SCRAM-SHA-256。如果 OpenSSL::Digest 支持摘要算法,则可以轻松为任何其他 SCRAM-* 机制添加新的身份验证器。

XOAUTH2

请参阅 XOAuth2Authenticator

使用用户名和 OAuth2 访问令牌登录。非标准且已被 OAUTHBEARER 废弃,但被广泛支持。

有关所有 SASL 机制及其规范的列表,请参阅 SASL 机制注册表。要注册新的身份验证器,请参阅 Authenticators

已弃用的机制

应避免使用过时的机制,但为了向后兼容仍然可用。请参阅 Net::IMAP::SASL 中已弃用的机制使用已弃用的机制会打印警告。

功能

"AUTH=#{mechanism}" 功能表示服务器支持的机制。在使用特定机制之前,请使用 auth_capable?auth_mechanisms 来检查是否支持。

if imap.auth_capable? "XOAUTH2"
  imap.authenticate "XOAUTH2", username, oauth2_access_token
elsif imap.auth_capable? "PLAIN"
  imap.authenticate "PLAIN", username, password
elsif !imap.capability? "LOGINDISABLED"
  imap.login username, password
else
  raise "No acceptable authentication mechanism is available"
end

尽管服务器应列出所有支持的 SASL 机制,但它们可能允许使用未列出的 mechanism 进行身份验证。

如果支持 [SASL-IR] 并且存在相应的 "AUTH=#{mechanism}" 功能,则可以将“初始响应”作为 AUTHENTICATE 命令的参数发送,从而节省一次往返。 SASL 交换允许服务器质询和客户端响应,但许多机制期望客户端首先“响应”。初始响应仅会为“客户端优先”机制发送。

服务器功能可能会在 starttlsloginauthenticate 之后更改。当此方法完成时,之前缓存的 capabilities 将被清除。如果 TaggedResponseauthenticate 包含更新的功能,则它们将被缓存。

# File net-imap-0.5.4/lib/net/imap.rb, line 1350
def authenticate(*args, sasl_ir: config.sasl_ir, **props, &callback)
  sasl_adapter.authenticate(*args, sasl_ir: sasl_ir, **props, &callback)
    .tap { @capabilities = capabilities_from_resp_code _1 }
end
capabilities() 点击以切换源代码

返回服务器功能。如果可用,则使用缓存的功能,而无需向服务器发送新的 capability 命令。

为了确保不区分大小写的比较,可以使用 capable?

注意: 大多数 Net::IMAP 方法当前不会根据服务器的 #capabilities 公告修改其行为。

有关 IMAP 功能的更多信息,请参阅 Net::IMAP 中的功能

相关:capable?, auth_capable?, auth_mechanisms, capability, enable

# File net-imap-0.5.4/lib/net/imap.rb, line 1026
def capabilities
  @capabilities || capability
end
capabilities_cached?() 点击以切换源代码

返回是否已缓存功能。如果为 true,则 capable?capabilities 不需要向服务器发送 capability 命令。

有关 IMAP 功能的更多信息,请参阅 Net::IMAP 中的功能

相关:capable?, capability, clear_cached_capabilities

# File net-imap-0.5.4/lib/net/imap.rb, line 1077
def capabilities_cached?
  !!@capabilities
end
capability() 点击以切换源代码

发送 CAPABILITY 命令 [IMAP4rev1 §6.1.1] 并返回服务器支持的功能数组。结果存储起来供 capable?capabilities 使用。

注意: 大多数 Net::IMAP 方法当前不会根据服务器的 #capabilities 公告修改其行为。

Net::IMAP 会根据 IMAP4rev2 §6.1.1§6.2§7.1 中的要求和建议自动存储和丢弃功能数据。使用 capable?auth_capable?capabilities 访问此缓存并避免不必要地发送 capability 命令。

有关 IMAP 功能的更多信息,请参阅 Net::IMAP 中的功能

相关:capable?, auth_capable?, capability, enable

# File net-imap-0.5.4/lib/net/imap.rb, line 1115
def capability
  synchronize do
    send_command("CAPABILITY")
    @capabilities = clear_responses("CAPABILITY").last.freeze
  end
end
capability?(capability)
别名为:capable?
capable?(capability) 点击以切换源代码

返回服务器是否支持给定的 capability。如果可用,则使用缓存的 capabilities,而无需向服务器发送新的 capability 命令。

注意: 大多数 Net::IMAP 方法当前不会根据服务器的 #capabilities 公告修改其行为。

有关 IMAP 功能的更多信息,请参阅 Net::IMAP 中的功能

相关:auth_capable?, capabilities, capability, enable

# File net-imap-0.5.4/lib/net/imap.rb, line 1012
def capable?(capability) capabilities.include? capability.to_s.upcase end
也别名为:capability?
check() 点击以切换源代码

发送 CHECK 命令 [IMAP4rev1 §6.4.1] 以请求当前选定邮箱的检查点。这将执行特定于实现的内务处理;例如,协调邮箱的内存和磁盘状态。

相关:idle, noop

# File net-imap-0.5.4/lib/net/imap.rb, line 1872
def check
  send_command("CHECK")
end
clear_cached_capabilities() 点击以切换源代码

清除 Net::IMAP 客户端记住的功能。这会强制在下次调用 capabilities 查询方法时发送 capability 命令。

Net::IMAP 的缓存功能可能发生更改时,会自动丢弃这些缓存的功能。显式调用此方法应该对于行为良好的服务器是不必要的。

相关:capable?, capability, capabilities_cached?

# File net-imap-0.5.4/lib/net/imap.rb, line 1090
def clear_cached_capabilities
  synchronize do
    clear_responses("CAPABILITY")
    @capabilities = nil
  end
end
clear_responses → hash 点击以切换源代码
clear_responses(type) → array

清除并返回未处理的 responses 哈希或单个响应 type 的未处理响应数组。

清除响应与其他线程同步。锁定在返回之前释放。

相关:extract_responses, responses, response_handlers

# File net-imap-0.5.4/lib/net/imap.rb, line 3014
def clear_responses(type = nil)
  synchronize {
    if type
      @responses.delete(type) || []
    else
      @responses.dup.transform_values(&:freeze)
        .tap { _1.default = [].freeze }
        .tap { @responses.clear }
    end
  }
    .freeze
end
close() 点击以切换源代码

发送 CLOSE 命令 [IMAP4rev1 §6.4.2] 以关闭当前选定的邮箱。CLOSE 命令会从邮箱中永久删除所有设置了 \Deleted 标志的消息。

相关:unselect

# File net-imap-0.5.4/lib/net/imap.rb, line 1882
def close
  send_command("CLOSE")
end
copy(set, mailbox) 点击以切换源代码

发送 COPY 命令 [IMAP4rev1 §6.4.7] 以将指定的消息复制到指定目标 mailbox 的末尾。 set 参数是一个数字、一个数字数组或一个 Range 对象。该数字是一个消息序列号。

相关:uid_copy

功能

如果支持 UIDPLUS [RFC4315],则服务器的响应应包含带有 UIDPlusDataCOPYUID 响应代码。这将报告目标邮箱的 UIDVALIDITY、源消息的 UID 集以及移动消息的已分配 UID 集。

# File net-imap-0.5.4/lib/net/imap.rb, line 2578
def copy(set, mailbox)
  copy_internal("COPY", set, mailbox)
end
create(mailbox) 点击以切换源代码

发送 CREATE 命令 [IMAP4rev1 §6.3.3] 以创建新的 mailbox

如果无法创建具有该名称的邮箱,则会引发 Net::IMAP::NoResponseError

相关:rename, delete

# File net-imap-0.5.4/lib/net/imap.rb, line 1456
def create(mailbox)
  send_command("CREATE", mailbox)
end
delete(mailbox) 点击以切换源代码

发送 DELETE 命令 [IMAP4rev1 §6.3.4] 以删除 mailbox

如果无法删除具有该名称的邮箱(因为它不存在或客户端没有删除权限),则会引发 Net::IMAP::NoResponseError

相关:create, rename

# File net-imap-0.5.4/lib/net/imap.rb, line 1468
def delete(mailbox)
  send_command("DELETE", mailbox)
end
disconnect() 点击以切换源代码

断开与服务器的连接。

相关:logout, logout!

# File net-imap-0.5.4/lib/net/imap.rb, line 973
def disconnect
  return if disconnected?
  begin
    begin
      # try to call SSL::SSLSocket#io.
      @sock.io.shutdown
    rescue NoMethodError
      # @sock is not an SSL::SSLSocket.
      @sock.shutdown
    end
  rescue Errno::ENOTCONN
    # ignore `Errno::ENOTCONN: Socket is not connected' on some platforms.
  rescue Exception => e
    @receiver_thread.raise(e)
  end
  @receiver_thread.join
  synchronize do
    @sock.close
  end
  raise e if e
end
disconnected?() 点击以切换源代码

如果与服务器断开连接,则返回 true。

相关:logout, disconnect

# File net-imap-0.5.4/lib/net/imap.rb, line 998
def disconnected?
  return @sock.closed?
end
enable(*capabilities) 点击以切换源代码

发送 ENABLE 命令 [RFC5161 §3.2] [IMAP4rev2 §6.3.1] 以启用指定的服务器 capabilities。每个功能可以是数组、字符串或符号。返回已启用的功能列表。

ENABLE 命令仅在已验证状态下有效,在选择任何邮箱之前。

相关:capable?, capabilities, capability

功能

服务器的功能必须包括 ENABLE [RFC5161] 或 IMAP4REV2 [RFC9051]。

此外,服务器功能必须包括与每个启用的扩展匹配的功能(通常与启用的扩展同名)。可以启用以下功能

CONDSTORE [RFC7162]

更新各种命令以返回 CONDSTORE 扩展响应。无需显式启用 CONDSTORE - 使用扩展定义的任何命令参数都会隐式启用它。请参阅 [RFC7162 §3.1]

:utf8"UTF8=ACCEPT" 的别名

在未来的版本中,enable(:utf8) 将启用 "UTF8=ACCEPT""IMAP4rev2",具体取决于服务器功能。

"UTF8=ACCEPT" [RFC6855]

服务器的功能必须包括 UTF8=ACCEPT UTF8=ONLY

这允许服务器发送以 UTF-8 编码的字符串,这些字符串可能需要使用 7 位编码,例如用于邮箱名称的 修改后的 UTF-7,或者用于消息头的 RFC2047 编码字。

注意: 未来的更新可能会略微不同的设置字符串编码,例如:当未启用 UTF-8 时使用 “US-ASCII”,启用时使用 “UTF-8”。目前,以 “quoted” 或 “text” 发送的字符串的编码将始终为 “UTF-8”,即使只使用了 ASCII 字符(例如,“Subject: Agenda”)。并且目前,服务器发送的字符串 “literals” 将始终具有 “ASCII-8BIT”(二进制)编码,即使它们通常包含 UTF-8 数据(如果它们是文本的话)。

"UTF8=ONLY" [RFC6855]

报告 UTF8=ONLY 功能的服务器要求客户端在选择任何邮箱之前 enable("UTF8=ACCEPT")。为方便起见,enable("UTF8=ONLY") 被别名为 enable("UTF8=ACCEPT")

不支持的功能

注意: 一些使用 ENABLE 的扩展允许服务器发送 Net::IMAP 无法解析的语法,这可能会引发异常并断开连接。某些扩展可能有效,但支持可能不完整、未经测试或处于实验阶段。

在某个功能被记录为支持之前,启用它可能会导致未记录的行为,并且未来的版本可能会在没有警告或弃用的情况下更新为不兼容的行为。

建议谨慎使用。

# File net-imap-0.5.4/lib/net/imap.rb, line 2789
def enable(*capabilities)
  capabilities = capabilities
    .flatten
    .map {|e| ENABLE_ALIASES[e] || e }
    .uniq
    .join(' ')
  synchronize do
    send_command("ENABLE #{capabilities}")
    result = clear_responses("ENABLED").last || []
    @utf8_strings ||= result.include? "UTF8=ACCEPT"
    @utf8_strings ||= result.include? "IMAP4REV2"
    result
  end
end
examine(mailbox, condstore: false) 点击切换源码

发送 EXAMINE 命令 [IMAP4rev1 §6.3.2] 以选择一个 mailbox,以便可以访问该 mailbox 中的消息。行为与 select 相同,只是选定的 mailbox 被标识为只读。

如果邮箱不存在或因某种原因无法检查,则会引发 Net::IMAP::NoResponseError

相关:select

# File net-imap-0.5.4/lib/net/imap.rb, line 1440
def examine(mailbox, condstore: false)
  args = ["EXAMINE", mailbox]
  args << ["CONDSTORE"] if condstore
  synchronize do
    @responses.clear
    send_command(*args)
  end
end
expunge → 消息序列号数组 点击切换源码
expunge → 已消失的 UID 数据

发送一个 EXPUNGE 命令 [IMAP4rev1 §6.4.3],以永久删除当前选定的邮箱中所有带有 \Deleted 标志的消息。

返回已清除消息的序列号数组,或者(当启用相应的功能时)返回已清除 UID 的 VanishedData。先前未处理的 EXPUNGEVANISHED 响应将与此命令的直接响应合并。VANISHED (EARLIER) 响应将被合并。

当没有消息被清除时,无论启用了哪些扩展,都将返回一个空数组。在未来的版本中,可能会返回一个空的 VanishedData,具体取决于当前启用的扩展。

相关:uid_expunge

功能

当启用 QRESYNCUIDONLY 时,expunge 返回 VanishedData,其中包含 UID——而不是消息序列号

# File net-imap-0.5.4/lib/net/imap.rb, line 1928
def expunge
  expunge_internal("EXPUNGE")
end
extract_responses(type) {|response| ... } → 数组 点击切换源码

为单个响应 type 生成所有未处理的 responses。删除并返回块返回真值的响应。

提取响应与其它线程同步。锁在返回之前释放。

相关:responses, clear_responses

# File net-imap-0.5.4/lib/net/imap.rb, line 3038
def extract_responses(type)
  type = String.try_convert(type) or
    raise ArgumentError, "type must be a string"
  raise ArgumentError, "must provide a block" unless block_given?
  extracted = []
  responses(type) do |all|
    all.reject! do |response|
      extracted << response if yield response
    end
  end
  extracted
end
fetch(set, attr, changedsince: nil) → FetchData 数组 点击切换源码

发送一个 FETCH 命令 [IMAP4rev1 §6.4.5] 以检索与邮箱中消息关联的数据。

set 是要获取的消息序列号,可以是 SequenceSet[…] 的任何有效输入。(对于 UID,请改用 uid_fetch。)

attr 是要获取的属性列表;有关有效属性列表,请参阅 FetchData 的文档。

changedsince 是一个可选的整数 mod-sequence。它将结果限制为 mod-sequence 大于 changedsince 的消息。

返回值是 FetchData 的数组。

相关:uid_fetch, FetchData

例如:

p imap.fetch(6..8, "UID")
#=> [#<Net::IMAP::FetchData seqno=6, attr={"UID"=>98}>, \\
     #<Net::IMAP::FetchData seqno=7, attr={"UID"=>99}>, \\
     #<Net::IMAP::FetchData seqno=8, attr={"UID"=>100}>]
p imap.fetch(6, "BODY[HEADER.FIELDS (SUBJECT)]")
#=> [#<Net::IMAP::FetchData seqno=6, attr={"BODY[HEADER.FIELDS (SUBJECT)]"=>"Subject: test\r\n\r\n"}>]
data = imap.uid_fetch(98, ["RFC822.SIZE", "INTERNALDATE"])[0]
p data.seqno
#=> 6
p data.attr["RFC822.SIZE"]
#=> 611
p data.attr["INTERNALDATE"]
#=> "12-Oct-2000 22:40:59 +0900"
p data.attr["UID"]
#=> 98

功能

许多扩展定义了新的消息 attr 名称。有关支持的扩展字段列表,请参阅 FetchData

服务器的功能必须包含 CONDSTORE [RFC7162] 才能使用 changedsince 参数。使用 changedsince 会隐式启用 CONDSTORE 扩展。

# File net-imap-0.5.4/lib/net/imap.rb, line 2437
def fetch(...)
  fetch_internal("FETCH", ...)
end
getacl(mailbox) 点击切换源码

发送一个 GETACL 命令 [RFC4314 §3.3] 以及指定的 mailbox。如果此邮箱存在,将返回包含 MailboxACLItem 对象的数组。

相关:setacl, MailboxACLItem

功能

服务器的功能必须包含 ACL [RFC4314]。

# File net-imap-0.5.4/lib/net/imap.rb, line 1736
def getacl(mailbox)
  synchronize do
    send_command("GETACL", mailbox)
    clear_responses("ACL").last
  end
end
getquota(mailbox) 点击切换源码

发送一个 GETQUOTA 命令 [RFC2087 §4.2] 以及指定的 mailbox。如果此邮箱存在,则返回一个包含 MailboxQuota 对象的数组。此命令通常只对服务器管理员可用。

相关:getquotaroot, setquota, MailboxQuota

功能

服务器的功能必须包含 QUOTA [RFC2087]。

# File net-imap-0.5.4/lib/net/imap.rb, line 1680
def getquota(mailbox)
  synchronize do
    send_command("GETQUOTA", mailbox)
    clear_responses("QUOTA")
  end
end
getquotaroot(mailbox) 点击切换源码

发送一个 GETQUOTAROOT 命令 [RFC2087 §4.3] 以及指定的 mailbox。此命令通常对管理员和用户都可用。如果此邮箱存在,则返回一个包含 MailboxQuotaRootMailboxQuota 类型对象的数组。

相关:getquota, setquota, MailboxQuotaRoot, MailboxQuota

功能

服务器的功能必须包含 QUOTA [RFC2087]。

# File net-imap-0.5.4/lib/net/imap.rb, line 1659
def getquotaroot(mailbox)
  synchronize do
    send_command("GETQUOTAROOT", mailbox)
    result = []
    result.concat(clear_responses("QUOTAROOT"))
    result.concat(clear_responses("QUOTA"))
    return result
  end
end
id(client_id=nil) 点击切换源码

发送一个 ID 命令 [RFC2971 §3.1] 并返回服务器响应的哈希值,如果服务器不标识自身,则返回 nil。

请注意,用户应首先检查服务器是否支持 ID 功能。例如

if capable?(:ID)
  id = imap.id(
    name: "my IMAP client (ruby)",
    version: MyIMAP::VERSION,
    "support-url": "mailto:[email protected]",
    os: RbConfig::CONFIG["host_os"],
  )
end

有关字段定义,请参阅 [ID]。

功能

服务器的功能必须包含 ID [RFC2971]。

# File net-imap-0.5.4/lib/net/imap.rb, line 1144
def id(client_id=nil)
  synchronize do
    send_command("ID", ClientID.new(client_id))
    clear_responses("ID").last
  end
end
idle(timeout = nil, &response_handler) 点击切换源码

发送一个 IDLE 命令 [RFC2177 §3] [IMAP4rev2 §6.3.13],该命令等待新消息或已删除消息的通知。在 IDLE 期间生成服务器的响应。

使用 idle_done 离开 IDLE。

如果给定 timeout,则此方法将在经过 timeout 秒后返回。timeout 可用于保持活动状态。例如,以下代码每 60 秒检查一次连接。

loop do
  imap.idle(60) do |response|
    do_something_with(response)
    imap.idle_done if some_condition?(response)
  end
end

返回服务器的响应,指示 IDLE 状态已结束。如果在 config.idle_response_timeout 秒内服务器未响应 idle_done,则返回 nil

相关:idle_done, noop, check

功能

服务器的功能必须包含 IDLE [RFC2177]。

# File net-imap-0.5.4/lib/net/imap.rb, line 2833
def idle(timeout = nil, &response_handler)
  raise LocalJumpError, "no block given" unless response_handler

  response = nil

  synchronize do
    tag = Thread.current[:net_imap_tag] = generate_tag
    put_string("#{tag} IDLE#{CRLF}")

    begin
      add_response_handler(&response_handler)
      @idle_done_cond = new_cond
      @idle_done_cond.wait(timeout)
      @idle_done_cond = nil
      if @receiver_thread_terminating
        raise @exception || Net::IMAP::Error.new("connection closed")
      end
    ensure
      unless @receiver_thread_terminating
        remove_response_handler(response_handler)
        put_string("DONE#{CRLF}")
        response = get_tagged_response(tag, "IDLE", idle_response_timeout)
      end
    end
  end

  return response
end
idle_done() 点击切换源码

离开 IDLE,允许 idle 返回。

如果服务器在 config.idle_response_timeout 秒内没有响应,idle 将返回 nil

相关:idle

# File net-imap-0.5.4/lib/net/imap.rb, line 2869
def idle_done
  synchronize do
    if @idle_done_cond.nil?
      raise Net::IMAP::Error, "not during IDLE"
    end
    @idle_done_cond.signal
  end
end
idle_response_timeout() 点击切换源码

等待接收 IDLE 响应的秒数。

# File net-imap-0.5.4/lib/net/imap.rb, line 796
def idle_response_timeout; config.idle_response_timeout end
list(refname, mailbox) 点击切换源码

发送一个 LIST 命令 [IMAP4rev1 §6.3.8] 并返回客户端可用的所有名称的完整集合中的名称子集。refname 提供上下文(例如,基于目录的邮箱层次结构中的基本目录)。mailbox 指定该上下文中指定的邮箱或(通过通配符)邮箱。mailbox 中可以使用两个通配符:"*",它匹配所有字符,包括层次结构分隔符(例如,在 UNIX 主机上的基于目录的邮箱层次结构中的“/”);和 "%",它匹配所有字符,不包括层次结构分隔符。

如果 refname 为空,则直接使用 mailbox 来确定要匹配哪些邮箱。如果 mailbox 为空,则返回 refname 的根名称和层次结构分隔符。

返回值是一个 MailboxList 数组。

相关:lsub, MailboxList

例如:

imap.create("foo/bar")
imap.create("foo/baz")
p imap.list("", "foo/%")
#=> [#<Net::IMAP::MailboxList attr=[:Noselect], delim="/", name="foo/">, \\
     #<Net::IMAP::MailboxList attr=[:Noinferiors, :Marked], delim="/", name="foo/bar">, \\
     #<Net::IMAP::MailboxList attr=[:Noinferiors], delim="/", name="foo/baz">]
# File net-imap-0.5.4/lib/net/imap.rb, line 1540
def list(refname, mailbox)
  synchronize do
    send_command("LIST", refname, mailbox)
    clear_responses("LIST")
  end
end
login(user, password) 点击切换源码

发送一个 LOGIN 命令 [IMAP4rev1 §6.2.3] 来标识客户端,并携带明文 password 以验证此 user 的身份。如果成功,连接将进入“已验证”状态。

使用 authenticate 应该优先login。LOGIN 命令与使用“LOGIN” mechanismauthenticate 不同。

如果身份验证失败,则会引发 Net::IMAP::NoResponseError 异常。

相关内容:authenticatestarttls

功能

当服务器通告 LOGINDISABLED 功能时,IMAP 客户端不得调用 login。默认情况下,当存在该功能时,Net::IMAP 将引发 LoginDisabledError 异常。请参阅 Config#enforce_logindisabled

starttlsloginauthenticate 后,服务器的功能可能会更改。在此方法完成后,必须使缓存的功能失效。loginTaggedResponse 可能在其 ResponseCode 中包含更新后的功能。

# File net-imap-0.5.4/lib/net/imap.rb, line 1381
def login(user, password)
  if enforce_logindisabled? && capability?("LOGINDISABLED")
    raise LoginDisabledError
  end
  send_command("LOGIN", user, password)
    .tap { @capabilities = capabilities_from_resp_code _1 }
end
logout() 点击以切换源代码

发送一个 LOGOUT 命令 [IMAP4rev1 §6.1.3],通知服务器客户端已完成连接。

相关内容:disconnectlogout!

# File net-imap-0.5.4/lib/net/imap.rb, line 1172
def logout
  send_command("LOGOUT")
end
logout!() 点击以切换源代码

调用 logout,然后在收到 LOGOUTTaggedResponse 后,调用 disconnect。返回来自 LOGOUTTaggedResponse。当客户端已断开连接时,返回 nil,这与引发异常的 logout 不同。

如果 logout 引发 StandardError 异常,将打印警告,但不会重新引发该异常。

这在必须断开连接的情况下非常有用,例如出于安全考虑或测试后。如果需要处理注销错误,请改用 logoutdisconnect

相关:logout, disconnect

# File net-imap-0.5.4/lib/net/imap.rb, line 1189
def logout!
  logout unless disconnected?
rescue => ex
  warn "%s during <Net::IMAP %s:%s> logout!: %s" % [
    ex.class, host, port, ex
  ]
ensure
  disconnect
end
lsub(refname, mailbox) 点击以切换源代码

发送一个 LSUB 命令 [IMAP4rev1 §6.3.9],并从用户声明为“活动”或“已订阅”的名称集合中返回一个名称子集。refnamemailbox 的解释与 list 相同。

返回值是一个 MailboxList 对象数组。

相关内容:subscribeunsubscribelistMailboxList

# File net-imap-0.5.4/lib/net/imap.rb, line 1751
def lsub(refname, mailbox)
  synchronize do
    send_command("LSUB", refname, mailbox)
    clear_responses("LSUB")
  end
end
move(set, mailbox) 点击以切换源代码

发送一个 MOVE 命令 [RFC6851 §3.1] [IMAP4rev2 §6.4.8],将指定的消息移动到指定目标 mailbox 的末尾。set 参数是一个数字、数字数组或 Range 对象。该数字是消息序列号。

相关内容:uid_move

功能

服务器的功能必须包含 MOVE [RFC6851]。

如果支持 UIDPLUS [RFC4315],则服务器的响应应包含带有 UIDPlusDataCOPYUID 响应代码。这将报告目标邮箱的 UIDVALIDITY、源消息的 UID 集以及移动消息的已分配 UID 集。

# File net-imap-0.5.4/lib/net/imap.rb, line 2614
def move(set, mailbox)
  copy_internal("MOVE", set, mailbox)
end
namespace() 点击以切换源代码

发送一个 NAMESPACE 命令 [RFC2342 §5],并返回可用的命名空间。NAMESPACE 命令允许客户端发现服务器用于个人邮箱、其他用户的邮箱和共享邮箱的命名空间前缀。

返回值是一个 Namespaces 对象,该对象具有 personalothershared 字段,每个字段都是一个 Namespace 对象数组。当服务器响应 nil 时,这些数组将为空。

许多 IMAP 服务器配置的默认个人命名空间为 ("" "/"):没有前缀和 “/” 层级分隔符。在那种常见情况下,朴素的客户端可能在命名邮箱时不会遇到任何问题。但是,许多服务器配置的默认个人命名空间为例如 ("INBOX." "."),将所有个人文件夹放在 INBOX 下,并以 “.” 作为层级分隔符。如果客户端不检查这一点,而是天真地假设它可以为所有服务器使用相同的文件夹名称,则文件夹创建(以及列表、移动等)可能会导致错误。

来自 RFC2342

尽管通常服务器只支持一个个人命名空间和一个其他用户的命名空间,但在某些情况下,可能存在多个这些命名空间,客户端必须为它们做好准备。如果配置客户端时需要创建某个邮箱,则在某些情况下,不清楚应在哪个个人命名空间中创建邮箱。在这些情况下,客户端应让用户选择在哪个命名空间中创建邮箱。

相关内容:listNamespacesNamespace

例如:

if capable?("NAMESPACE")
  namespaces = imap.namespace
  if namespace = namespaces.personal.first
    prefix = namespace.prefix  # e.g. "" or "INBOX."
    delim  = namespace.delim   # e.g. "/" or "."
    # personal folders should use the prefix and delimiter
    imap.create(prefix + "foo")
    imap.create(prefix + "bar")
    imap.create(prefix + %w[path to my folder].join(delim))
  end
end

功能

服务器的功能必须包含 NAMESPACE [RFC2342]。

# File net-imap-0.5.4/lib/net/imap.rb, line 1597
def namespace
  synchronize do
    send_command("NAMESPACE")
    clear_responses("NAMESPACE").last
  end
end
noop() 点击以切换源代码

向服务器发送一个 NOOP 命令 [IMAP4rev1 §6.1.2]

这允许服务器发送未经请求的未标记的 EXPUNGE responses,但不执行任何客户端请求。允许 IMAP 服务器随时发送未经请求的未标记响应,但 EXPUNGE 除外。

相关内容:idlecheck

# File net-imap-0.5.4/lib/net/imap.rb, line 1163
def noop
  send_command("NOOP")
end
open_timeout() 点击以切换源代码

等待连接打开的秒数。如果 IMAP 对象无法在此时间内打开连接,则会引发 Net::OpenTimeout 异常。默认值为 30 秒。

# File net-imap-0.5.4/lib/net/imap.rb, line 793
def open_timeout; config.open_timeout end
remove_response_handler(handler) 点击以切换源代码

删除响应处理程序。

相关内容:add_response_handlerresponse_handlers

# File net-imap-0.5.4/lib/net/imap.rb, line 3091
def remove_response_handler(handler)
  synchronize do
    @response_handlers.delete(handler)
  end
end
rename(mailbox, newname) 点击以切换源代码

发送一个 RENAME 命令 [IMAP4rev1 §6.3.5],将 mailbox 的名称更改为 newname

如果由于任何原因无法将名为 mailbox 的邮箱重命名为 newname,例如,因为 mailbox 不存在,或者因为已经存在名为 newname 的邮箱,则会引发 Net::IMAP::NoResponseError 异常。

相关内容:createdelete

# File net-imap-0.5.4/lib/net/imap.rb, line 1481
def rename(mailbox, newname)
  send_command("RENAME", mailbox, newname)
end
response_handlers() 点击以切换源代码

返回所有响应处理程序,包括由命令内部添加的响应处理程序。每个响应处理程序都将使用每个新的 UntaggedResponseTaggedResponseContinuationRequest 来调用。

响应处理程序在接收器线程内的互斥锁中调用。无法处理新的响应,来自其他线程的命令必须等到所有 response_handlers 返回后才能继续。异常将关闭接收器线程并关闭连接。

为了线程安全,返回的数组是内部数组的冻结副本。

相关内容:add_response_handlerremove_response_handler

# File net-imap-0.5.4/lib/net/imap.rb, line 3064
def response_handlers
  synchronize { @response_handlers.clone.freeze }
end
responses -> {String → Array} 的哈希值(请参阅 config.responses_without_block) 点击以切换源代码
responses(type) → 冻结数组
responses {|hash| ...} → 代码块结果
responses(type) {|array| ...} → 代码块结果

生成或返回未处理的服务器响应。未处理的响应存储在哈希中,其中 UntaggedResponse#data 的数组按 UntaggedResponse#name 键控,而 非-nil 未标记的 ResponseCode#dataResponseCode#name 键控。

当给定代码块时,生成未处理的响应并返回代码块的结果。如果没有代码块,则返回未处理的响应。

使用 type

仅生成或返回该 type 的响应数组。当未给定代码块时,返回的数组是冻结的副本。

没有 type

生成或返回整个响应哈希。

当未给定代码块时,其行为由 Config#responses_without_block 确定。

:silence_deprecation_warning (原始行为)

返回可变的响应哈希(没有任何警告)。这不是线程安全的。

:warn (自 v0.5 以来的默认值)

打印警告并返回可变响应哈希。这并非线程安全。

:frozen_dup (计划在 v0.6 中默认为此项)

返回未处理响应哈希的冻结副本,其中包含冻结的数组值。

:raise

引发带有弃用警告的 ArgumentError

例如

imap.select("inbox")
p imap.responses("EXISTS").last
#=> 2
p imap.responses("UIDNEXT", &:last)
#=> 123456
p imap.responses("UIDVALIDITY", &:last)
#=> 968263756
p imap.responses {|responses|
  {
    exists:      responses.delete("EXISTS").last,
    uidnext:     responses.delete("UIDNEXT").last,
    uidvalidity: responses.delete("UIDVALIDITY").last,
  }
}
#=> {:exists=>2, :uidnext=>123456, :uidvalidity=>968263756}
# "EXISTS", "UIDNEXT", and "UIDVALIDITY" have been removed:
p imap.responses(&:keys)
#=> ["FLAGS", "OK", "PERMANENTFLAGS", "RECENT", "HIGHESTMODSEQ"]

相关:extract_responses, clear_responses, response_handlers, greeting

线程安全

注意: 对响应哈希的访问是同步的,以确保线程安全。接收线程和 response_handlers 在代码块完成之前无法处理新的响应。在代码块之外访问响应哈希或其响应类型数组是不安全的。它们可以在代码块内安全地更新。请考虑使用 clear_responsesextract_responses 代替。

Net::IMAP 将在调用命令的线程和接收线程中添加和删除响应哈希及其数组值中的响应,但不会在将响应添加到响应哈希后修改任何响应。

清除响应

先前未处理的响应会在使用 selectexamine 进入邮箱之前自动清除。长时间存在的连接可能会收到许多未处理的服务器响应,必须对其进行修剪,否则它们将持续消耗更多内存。在代码块内更新或清除响应哈希或数组,或使用 extract_responsesclear_responsesadd_response_handler 删除响应。

缺少响应

仅存储非 nil 数据。许多重要的响应代码本身没有数据,但被用作附加到的 ResponseText 对象的“标签”。ResponseText 将可通过其响应类型访问:“OK”、“NO”、“BAD”、“BYE”或“PREAUTH”。

TaggedResponse#data 不会保存到 responses 中,也不会保存标记响应上的任何 ResponseCode#data。尽管某些命令方法会直接返回 TaggedResponse,但必须使用 add_response_handler 来处理所有响应代码。

# File net-imap-0.5.4/lib/net/imap.rb, line 2980
def responses(type = nil)
  if block_given?
    synchronize { yield(type ? @responses[type.to_s.upcase] : @responses) }
  elsif type
    synchronize { @responses[type.to_s.upcase].dup.freeze }
  else
    case config.responses_without_block
    when :raise
      raise ArgumentError, RESPONSES_DEPRECATION_MSG
    when :warn
      warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated)
    when :frozen_dup
      synchronize {
        responses = @responses.transform_values(&:freeze)
        responses.default_proc = nil
        responses.default = [].freeze
        return responses.freeze
      }
    end
    @responses
  end
end
select(mailbox, condstore: false) 点击以切换源

发送 SELECT 命令 [IMAP4rev1 §6.3.1],以选择 mailbox,以便可以访问 mailbox 中的消息。

选择邮箱后,您可以从 imap.responses("EXISTS", &:last) 中检索该邮箱中的项目数,并从 imap.responses("RECENT", &:last) 中检索最近的消息数。请注意,如果会话期间有新消息到达或现有消息被删除,这些值可能会更改;请参阅 add_response_handler 以了解如何检测这些事件。

condstore 关键字参数为 true 时,会告知服务器启用该扩展。如果 mailbox 支持 mod-sequences 的持久性,则 HIGHESTMODSEQ ResponseCode 将作为未标记的响应发送到 select,并且所有 'FETCH` 响应都将包含 FetchData#modseq。否则,将发送 NOMODSEQ ResponseCode

如果邮箱不存在或由于某种原因不可选,则会引发 Net::IMAP::NoResponseError

相关:examine

功能

如果支持 [UIDPLUS],服务器可能会返回带有 “UIDNOTSTICKY” 响应代码的未标记的“NO”响应,表明邮件存储不支持持久 UID

imap.responses("NO", &:last)&.code&.name == "UIDNOTSTICKY"

如果支持 [CONDSTORE],则可以使用 condstore 关键字参数。

imap.select("mbox", condstore: true)
modseq = imap.responses("HIGHESTMODSEQ", &:last)
# File net-imap-0.5.4/lib/net/imap.rb, line 1422
def select(mailbox, condstore: false)
  args = ["SELECT", mailbox]
  args << ["CONDSTORE"] if condstore
  synchronize do
    @responses.clear
    send_command(*args)
  end
end
setacl(mailbox, user, rights) 点击以切换源

发送 SETACL 命令 [RFC4314 §3.1],其中包含 mailboxuser 和用户在该邮箱上拥有的 rights。如果 rights 为 nil,则该用户将被剥夺对该邮箱的任何权利。

相关:getacl

功能

服务器的功能必须包含 ACL [RFC4314]。

# File net-imap-0.5.4/lib/net/imap.rb, line 1718
def setacl(mailbox, user, rights)
  if rights.nil?
    send_command("SETACL", mailbox, user, "")
  else
    send_command("SETACL", mailbox, user, rights)
  end
end
setquota(mailbox, quota) 点击以切换源

发送 SETQUOTA 命令 [RFC2087 §4.1],其中包含指定的 mailboxquota。如果 quota 为 nil,则该邮箱的 quota 将被取消设置。通常,需要以服务器管理员身份登录才能使其工作。

相关:getquotagetquotaroot

功能

服务器的功能必须包含 QUOTA [RFC2087]。

# File net-imap-0.5.4/lib/net/imap.rb, line 1698
def setquota(mailbox, quota)
  if quota.nil?
    data = '()'
  else
    data = '(STORAGE ' + quota.to_s + ')'
  end
  send_command("SETQUOTA", mailbox, RawData.new(data))
end
sort(sort_keys, search_keys, charset) 点击以切换源

发送 SORT 命令 [RFC5256 §3],以搜索与 search_keys 匹配的邮箱消息,并返回按 sort_keys 排序的消息序列号数组。 search_keys 的解释方式与 search 相同。

相关:uid_sortsearchuid_searchthreaduid_thread

例如:

p imap.sort(["FROM"], ["ALL"], "US-ASCII")
#=> [1, 2, 3, 5, 6, 7, 8, 4, 9]
p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII")
#=> [6, 7, 8, 1]

功能

服务器的功能必须包含 SORT [RFC5256]。

# File net-imap-0.5.4/lib/net/imap.rb, line 2658
def sort(sort_keys, search_keys, charset)
  return sort_internal("SORT", sort_keys, search_keys, charset)
end
starttls(**options) 点击以切换源

发送 STARTTLS 命令 [IMAP4rev1 §6.2.1] 以启动 TLS 会话。

任何 options 都将直接转发到 OpenSSL::SSL::SSLContext#set_params;键是 SSLContext 上属性分配方法的名称。

有关已弃用的参数,请参阅 DeprecatedClientOptions#starttls

此方法在 TLS 协商和主机名验证都成功后返回。任何错误都表示连接未被安全保护。

注意

在 STARTTLS 之前添加的任何 response_handlers 都应该意识到,发送给 STARTTLS 的 TaggedResponse 是以明文形式发送的, TLS 协商之前。TLS 在该响应之后立即开始。随响应发送的任何响应代码(例如 CAPABILITY)都是不安全的,不能信任。

相关:Net::IMAP.newloginauthenticate

功能

除非服务器声明了 STARTTLS 功能,否则客户端不应调用 starttls

服务器功能在 starttlsloginauthenticate 之后可能会发生变化。当此方法完成时,缓存的 capabilities 将被清除。

# File net-imap-0.5.4/lib/net/imap.rb, line 1229
def starttls(**options)
  @ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
  send_command("STARTTLS") do |resp|
    if resp.kind_of?(TaggedResponse) && resp.name == "OK"
      clear_cached_capabilities
      clear_responses
      start_tls_session
    end
  end
end
status(mailbox, attr) 点击以切换源代码

发送 STATUS 命令 [IMAP4rev1 §6.3.10] 并返回指示的 mailbox 的状态。attr 是一个或多个要请求状态的属性列表。

返回值是一个属性哈希。大多数状态属性返回整数值,但有些返回其他值类型(如下所述)。

如果无法返回 mailbox 的状态值,则会引发 Net::IMAP::NoResponseError;例如,因为它不存在。

支持的属性

MESSAGES

邮箱中的消息数。

UIDNEXT

邮箱的下一个唯一标识符值。

UIDVALIDITY

邮箱的唯一标识符有效性值。

UNSEEN

不带 \Seen 标志的消息数。

DELETED

带有 \Deleted 标志的消息数。

SIZE

邮箱的大概大小—必须大于或等于所有消息的 RFC822.SIZE 获取项值的总和。

HIGHESTMODSEQ

邮箱中所有消息的最高修改序列值。请参阅 CONDSTORE [RFC7162]

MAILBOXID

服务器分配的邮箱唯一字符串标识符。请参阅 OBJECTID [RFC8474]

RECENT

带有 \Recent 标志的消息数。注意:RECENT 已从 IMAP4rev2 中删除。

可以请求不支持的属性。属性值将为整数或 ExtensionData 对象。

例如:

p imap.status("inbox", ["MESSAGES", "RECENT"])
#=> {"RECENT"=>0, "MESSAGES"=>44}

功能

SIZE 要求服务器的功能包括 IMAP4rev2STATUS=SIZE [RFC8483]

DELETED 要求服务器的功能包括 IMAP4rev2

HIGHESTMODSEQ 要求服务器的功能包括 CONDSTORE [RFC7162]

MAILBOXID 要求服务器的功能包括 OBJECTID [RFC8474]

# File net-imap-0.5.4/lib/net/imap.rb, line 1818
def status(mailbox, attr)
  synchronize do
    send_command("STATUS", mailbox, attr)
    clear_responses("STATUS").last&.attr
  end
end
store(set, attr, value, unchangedsince: nil) → FetchData 数组 点击以切换源代码

发送 STORE 命令 [IMAP4rev1 §6.4.6] 以更改邮箱中与消息关联的数据,特别是它们的标志。

set 是一个数字、一个数字数组或一个 Range 对象。每个数字都是一个消息序列号。

attr 是要存储的数据项的名称。value 的语义根据 attr 而变化。

  • attr"FLAGS" 时,value 中的标志将替换消息的标志列表。

  • attr"+FLAGS" 时,value 中的标志将添加到消息的标志中。

  • attr"-FLAGS" 时,value 中的标志将从消息中删除。

unchangedsince 是一个可选的整数修改序列。它禁止对 mod-sequence 大于指定的 unchangedsince 值的消息进行任何更改。如果任何消息未能通过此检查,则会在 MODIFIED ResponseCode 中返回 SequenceSet

返回值是 FetchData 的数组。

相关:uid_store

例如:

p imap.store(6..8, "+FLAGS", [:Deleted])
#=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>,
     #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>,
     #<Net::IMAP::FetchData seqno=8, attr={"FLAGS"=>[:Seen, :Deleted]}>]

功能

扩展可以定义新的数据项以与 store 一起使用。

服务器的功能必须包括 CONDSTORE [RFC7162] 才能使用 unchangedsince 参数。使用 unchangedsince 会隐式启用 CONDSTORE 扩展。

# File net-imap-0.5.4/lib/net/imap.rb, line 2542
def store(set, attr, flags, unchangedsince: nil)
  store_internal("STORE", set, attr, flags, unchangedsince: unchangedsince)
end
subscribe(mailbox) 点击以切换源代码

发送 SUBSCRIBE 命令 [IMAP4rev1 §6.3.6],将指定的 mailbox 名称添加到服务器的“活动”或“已订阅”邮箱集中,如 lsub 返回的那样。

如果无法订阅 mailbox,则会引发 Net::IMAP::NoResponseError;例如,因为它不存在。

相关:unsubscribelsublist

# File net-imap-0.5.4/lib/net/imap.rb, line 1493
def subscribe(mailbox)
  send_command("SUBSCRIBE", mailbox)
end
thread(algorithm, search_keys, charset) 点击以切换源代码

发送 THREAD 命令 [RFC5256 §3] 来搜索邮箱并以线程格式返回消息序列号,作为 ThreadMember 树。search_keys 的解释与 search 的解释相同。

支持的算法包括

ORDEREDSUBJECT

根据主题拆分为单级线程,按日期排序。

REFERENCES

通过父/子关系拆分为线程,该关系由哪个消息是哪个消息的回复来确定。

search 不同,charset 是必需的参数。US-ASCII 和 UTF-8 是示例值。

相关:uid_threadsearchuid_searchsortuid_sort

功能

服务器的功能必须包括 THREAD [RFC5256]。

# File net-imap-0.5.4/lib/net/imap.rb, line 2698
def thread(algorithm, search_keys, charset)
  return thread_internal("THREAD", algorithm, search_keys, charset)
end
tls_verified?() 点击以切换源代码

在 TLS 协商完成并已验证远程主机名后返回 true。当已建立 TLS 但禁用对等验证时返回 false。

# File net-imap-0.5.4/lib/net/imap.rb, line 968
def tls_verified?; @tls_verified end
uid_copy(set, mailbox) 点击以切换源代码

发送 UID COPY 命令 [IMAP4rev1 §6.4.8],将指定的消息复制到指定目标 mailbox 的末尾。

类似于 copy,但 set 包含唯一标识符。

功能

UIDPLUSuid_copy 的影响与对 copy 的影响相同。

# File net-imap-0.5.4/lib/net/imap.rb, line 2591
def uid_copy(set, mailbox)
  copy_internal("UID COPY", set, mailbox)
end
uid_expunge{uid_set) → 消息序列号数组 点击以切换源代码
uid_expunge{uid_set) → UIDs 的 VanishedData

发送 UID EXPUNGE 命令 [RFC4315 §2.1] [IMAP4rev2 §6.4.9] 以永久删除所有同时设置了 \Deleted 标志且 UID 包含在 uid_set 中的消息。

返回与 expunge 相同的结果类型。

通过在与服务器重新同步时使用 uid_expunge 而不是 expunge,客户端可以确保它不会无意中删除在客户端上次连接和客户端重新同步之间由其他客户端标记为 \Deleted 的任何消息。

相关:expunge

功能

服务器的功能必须包括 IMAP4rev2UIDPLUS [RFC4315]。

否则,uid_expunge 将以与 expunge 相同的方式通过扩展进行更新。

# File net-imap-0.5.4/lib/net/imap.rb, line 1958
def uid_expunge(uid_set)
  expunge_internal("UID EXPUNGE", SequenceSet.new(uid_set))
end
uid_fetch(set, attr, changedsince: nil, partial: nil) → FetchData 数组 点击以切换源代码

发送 UID FETCH 命令 [IMAP4rev1 §6.4.8] 以检索邮箱中与消息关联的数据。

set 是要获取的消息 UID,并且可以是 SequenceSet[…] 的任何有效输入。(对于消息序列号,请改用 fetch。)

attr 的行为与 fetch 相同。

注意: 服务器必须隐式地将 UID 消息数据项作为任何由 UID 命令引起的 FETCH 响应的一部分,无论是否将 UID 指定为 FETCH 的消息数据项。

changedsince(可选)的行为与 fetch 相同。

partial 是一个可选的范围,用于限制返回结果的数量。当 set 包含未知数量的消息时,它非常有用。1..500 返回 set 中的前 500 条消息(按邮箱顺序),501..1000 返回第二个 500 条消息,依此类推。partial 也可以是负数:-500..-1 选择 set 中的最后 500 条消息。需要 PARTIAL 功能。 [RFC9394]

例如

# Without partial, the size of the results may be unknown beforehand:
results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS))
# ... maybe wait for a long time ... and allocate a lot of memory ...
results.size # => 0..2**32-1
process results # may also take a long time and use a lot of memory...

# Using partial, the results may be paginated:
loop do
  results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS),
                           partial: 1..500)
  # fetch should return quickly and allocate little memory
  results.size # => 0..500
  break if results.empty?
  next_uid_to_fetch = results.last.uid + 1
  process results
end

相关:fetch, FetchData

功能

服务器的功能必须包括 PARTIAL [RFC9394] 才能使用 partial 参数。

否则,与 fetch 相同。

# File net-imap-0.5.4/lib/net/imap.rb, line 2495
def uid_fetch(...)
  fetch_internal("UID FETCH", ...)
end
uid_move(set, mailbox) 点击切换源代码

发送一个 UID MOVE 命令 [RFC6851 §3.2] [IMAP4rev2 §6.4.9],将指定的消息移动到指定的目标 mailbox 的末尾。

move 类似,但 set 包含唯一标识符。

相关:move

功能

move 相同:服务器的功能必须包括 MOVE [RFC6851]。UIDPLUS 也会以影响 move 的相同方式影响 uid_move

# File net-imap-0.5.4/lib/net/imap.rb, line 2632
def uid_move(set, mailbox)
  copy_internal("UID MOVE", set, mailbox)
end
uid_sort(sort_keys, search_keys, charset) 点击切换源代码

发送一个 UID SORT 命令 [RFC5256 §3],在邮箱中搜索匹配 search_keys 的消息,并返回一个按 sort_keys 排序的唯一标识符数组。search_keys 的解释方式与 search 相同。

相关:sort, search, uid_search, thread, uid_thread

功能

服务器的功能必须包含 SORT [RFC5256]。

# File net-imap-0.5.4/lib/net/imap.rb, line 2673
def uid_sort(sort_keys, search_keys, charset)
  return sort_internal("UID SORT", sort_keys, search_keys, charset)
end
uid_store(set, attr, value, unchangedsince: nil) → array of FetchData 点击切换源代码

发送一个 UID STORE 命令 [IMAP4rev1 §6.4.8],以更改与邮箱中消息关联的数据,特别是它们的标志。

store 类似,但 set 包含唯一标识符而不是消息序列号。

相关:store

功能

store 相同。

# File net-imap-0.5.4/lib/net/imap.rb, line 2560
def uid_store(set, attr, flags, unchangedsince: nil)
  store_internal("UID STORE", set, attr, flags, unchangedsince: unchangedsince)
end
uid_thread(algorithm, search_keys, charset) 点击切换源代码

发送一个 UID THREAD 命令 [RFC5256 §3],类似于 thread,但返回唯一标识符而不是消息序列号。

相关:thread, search, uid_search, sort, uid_sort

功能

服务器的功能必须包括 THREAD [RFC5256]。

# File net-imap-0.5.4/lib/net/imap.rb, line 2712
def uid_thread(algorithm, search_keys, charset)
  return thread_internal("UID THREAD", algorithm, search_keys, charset)
end
unselect() 点击切换源代码

发送一个 UNSELECT 命令 [RFC3691 §2] [IMAP4rev2 §6.4.2],以释放邮箱的会话资源并返回到“已认证”状态。这与 close 相同,只是 \Deleted 消息不会从邮箱中删除。

相关:close

功能

服务器的功能必须包括 UNSELECT [RFC3691]。

# File net-imap-0.5.4/lib/net/imap.rb, line 1898
def unselect
  send_command("UNSELECT")
end
unsubscribe(mailbox) 点击切换源代码

发送一个 UNSUBSCRIBE 命令 [IMAP4rev1 §6.3.7],以从服务器的“活动”或“订阅”邮箱集合中删除指定的 mailbox 名称。

如果无法取消订阅 mailbox,例如,因为客户端当前未订阅它,则会引发 Net::IMAP::NoResponseError

相关:subscribe, lsub, list

# File net-imap-0.5.4/lib/net/imap.rb, line 1506
def unsubscribe(mailbox)
  send_command("UNSUBSCRIBE", mailbox)
end
xlist(refname, mailbox) 点击切换源代码

发送一个 XLIST 命令,并从客户端可用的所有名称的完整集合中返回一个名称子集。refname 提供一个上下文(例如,基于目录的邮箱层次结构中的基本目录)。mailbox 指定该上下文下的一个或多个邮箱(通过通配符)。可以在 mailbox 中使用两个通配符:“*”,它匹配所有字符,包括层次结构分隔符(例如,在基于 UNIX 的目录托管的邮箱层次结构上使用“/”);和“%”,它匹配所有字符,除了层次结构分隔符。

如果 refname 为空,则直接使用 mailbox 来确定要匹配哪些邮箱。如果 mailbox 为空,则返回 refname 的根名称和层次结构分隔符。

XLIST 命令类似于 LIST 命令,只是返回的标志引用文件夹/邮箱的功能,例如 :Sent

返回值是 MailboxList 对象的数组。例如

imap.create("foo/bar")
imap.create("foo/baz")
p imap.xlist("", "foo/%")
#=> [#<Net::IMAP::MailboxList attr=[:Noselect], delim="/", name="foo/">, \\
     #<Net::IMAP::MailboxList attr=[:Noinferiors, :Marked], delim="/", name="foo/bar">, \\
     #<Net::IMAP::MailboxList attr=[:Noinferiors], delim="/", name="foo/baz">]

相关:list, MailboxList

功能

服务器的功能必须包括 XLIST,这是一个已弃用的 Gmail 扩展(已被 SPECIAL-USE 取代)。

# File net-imap-0.5.4/lib/net/imap.rb, line 1641
def xlist(refname, mailbox)
  synchronize do
    send_command("XLIST", refname, mailbox)
    clear_responses("XLIST")
  end
end

私有实例方法

build_ssl_ctx(ssl) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3533
def build_ssl_ctx(ssl)
  if ssl
    params = (Hash.try_convert(ssl) || {}).freeze
    context = SSLContext.new
    context.set_params(params)
    if defined?(VerifyCallbackProc)
      context.verify_callback = VerifyCallbackProc
    end
    context.freeze
    [params, context]
  else
    false
  end
end
capabilities_from_resp_code(resp) 点击切换源代码

注意:仅对问候语、登录和身份验证调用此方法

# File net-imap-0.5.4/lib/net/imap.rb, line 3270
def capabilities_from_resp_code(resp)
  return unless %w[PREAUTH OK].any? { _1.casecmp? resp.name }
  return unless (code = resp.data.code)
  return unless code.name.casecmp?("CAPABILITY")
  code.data.freeze
end
coerce_search_arg_to_seqset?(obj) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3515
def coerce_search_arg_to_seqset?(obj)
  case obj
  when Set, -1, :* then true
  when Range       then true
  when Array       then obj.all? { coerce_search_array_arg_to_seqset? _1 }
  else                  obj.respond_to?(:to_sequence_set)
  end
end
coerce_search_array_arg_to_seqset?(obj) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3524
def coerce_search_array_arg_to_seqset?(obj)
  case obj
  when Integer then obj.positive? || obj == -1
  when String  then ResponseParser::Patterns::SEQUENCE_SET_STR.match?(obj.b)
  else
    coerce_search_arg_to_seqset?(obj)
  end
end
convert_return_opts(unconverted) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3401
def convert_return_opts(unconverted)
  return_opts = Array.try_convert(unconverted) or
    raise TypeError, "expected return options to be Array, got %s" % [
      unconverted.class
    ]
  return_opts.map {|opt|
    case opt
    when Symbol                 then opt.to_s
    when PartialRange::Negative then PartialRange[opt]
    when Range                  then SequenceSet[opt]
    else                             opt
    end
  }
end
copy_internal(cmd, set, mailbox) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3484
def copy_internal(cmd, set, mailbox)
  send_command(cmd, SequenceSet.new(set), mailbox)
end
enforce_logindisabled?() 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3339
def enforce_logindisabled?
  if config.enforce_logindisabled == :when_capabilities_cached
    capabilities_cached?
  else
    config.enforce_logindisabled
  end
end
expunge_internal(...) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3347
def expunge_internal(...)
  synchronize do
    send_command(...)
    expunged_array = clear_responses("EXPUNGE")
    vanished_array = extract_responses("VANISHED") { !_1.earlier? }
    if vanished_array.empty?
      expunged_array
    elsif vanished_array.length == 1
      vanished_array.first
    else
      merged_uids = SequenceSet[*vanished_array.map(&:uids)]
      VanishedData[uids: merged_uids, earlier: false]
    end
  end
end
fetch_internal(cmd, set, attr, mod = nil, partial: nil, changedsince: nil) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3442
def fetch_internal(cmd, set, attr, mod = nil, partial: nil, changedsince: nil)
  set = SequenceSet[set]
  if partial
    mod ||= []
    mod << "PARTIAL" << PartialRange[partial]
  end
  if changedsince
    mod ||= []
    mod << "CHANGEDSINCE" << Integer(changedsince)
  end
  case attr
  when String then
    attr = RawData.new(attr)
  when Array then
    attr = attr.map { |arg|
      arg.is_a?(String) ? RawData.new(arg) : arg
    }
  end

  synchronize do
    clear_responses("FETCH")
    if mod
      send_command(cmd, set, attr, mod)
    else
      send_command(cmd, set, attr)
    end
    clear_responses("FETCH")
  end
end
generate_tag() 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3319
def generate_tag
  @tagno += 1
  return format("%s%04d", @tag_prefix, @tagno)
end
get_response() 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3233
def get_response
  buff = String.new
  while true
    s = @sock.gets(CRLF)
    break unless s
    buff.concat(s)
    if /\{(\d+)\}\r\n/n =~ s
      s = @sock.read($1.to_i)
      buff.concat(s)
    else
      break
    end
  end
  return nil if buff.length == 0
  if config.debug?
    $stderr.print(buff.gsub(/^/n, "S: "))
  end
  return @parser.parse(buff)
end
get_server_greeting() 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3112
def get_server_greeting
  greeting = get_response
  raise Error, "No server greeting - connection closed" unless greeting
  record_untagged_response_code greeting
  raise ByeResponseError, greeting if greeting.name == "BYE"
  greeting
end
get_tagged_response(tag, cmd, timeout = nil) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3205
def get_tagged_response(tag, cmd, timeout = nil)
  if timeout
    deadline = Time.now + timeout
  end
  until @tagged_responses.key?(tag)
    raise @exception if @exception
    if timeout
      timeout = deadline - Time.now
      if timeout <= 0
        return nil
      end
    end
    @tagged_response_arrival.wait(timeout)
  end
  resp = @tagged_responses.delete(tag)
  case resp.name
  when /\A(?:OK)\z/ni
    return resp
  when /\A(?:NO)\z/ni
    raise NoResponseError, resp
  when /\A(?:BAD)\z/ni
    raise BadResponseError, resp
  else
    disconnect
    raise InvalidResponseError, "invalid tagged resp: %p" % [resp.raw.chomp]
  end
end
normalize_searching_criteria(criteria) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3504
def normalize_searching_criteria(criteria)
  return [RawData.new(criteria)] if criteria.is_a?(String)
  criteria.map {|i|
    if coerce_search_arg_to_seqset?(i)
      SequenceSet[i]
    else
      i
    end
  }
end
put_string(str) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3324
def put_string(str)
  @sock.print(str)
  if config.debug?
    if @debug_output_bol
      $stderr.print("C: ")
    end
    $stderr.print(str.gsub(/\n/n) { $'.empty? ? $& : "\nC: " })
    if /\n\z/n.match(str)
      @debug_output_bol = true
    else
      @debug_output_bol = false
    end
  end
end
receive_responses() 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3138
def receive_responses
  connection_closed = false
  until connection_closed
    synchronize do
      @exception = nil
    end
    begin
      resp = get_response
    rescue Exception => e
      synchronize do
        @sock.close
        @exception = e
      end
      break
    end
    unless resp
      synchronize do
        @exception = EOFError.new("end of file reached")
      end
      break
    end
    begin
      synchronize do
        case resp
        when TaggedResponse
          @tagged_responses[resp.tag] = resp
          @tagged_response_arrival.broadcast
          case resp.tag
          when @logout_command_tag
            return
          when @continued_command_tag
            @continuation_request_exception =
              RESPONSE_ERRORS[resp.name].new(resp)
            @continuation_request_arrival.signal
          end
        when UntaggedResponse
          record_untagged_response(resp)
          if resp.name == "BYE" && @logout_command_tag.nil?
            @sock.close
            @exception = ByeResponseError.new(resp)
            connection_closed = true
          end
        when ContinuationRequest
          @continuation_request_arrival.signal
        end
        @response_handlers.each do |handler|
          handler.call(resp)
        end
      end
    rescue Exception => e
      @exception = e
      synchronize do
        @tagged_response_arrival.broadcast
        @continuation_request_arrival.broadcast
      end
    end
  end
  synchronize do
    @receiver_thread_terminating = true
    @tagged_response_arrival.broadcast
    @continuation_request_arrival.broadcast
    if @idle_done_cond
      @idle_done_cond.signal
    end
  end
end
record_untagged_response(resp) 点击切换源代码

存储 name => […, data]

# File net-imap-0.5.4/lib/net/imap.rb, line 3257
def record_untagged_response(resp)
  @responses[resp.name] << resp.data
  record_untagged_response_code resp
end
record_untagged_response_code(resp) 点击切换源代码

存储 code.name => […, code.data]

# File net-imap-0.5.4/lib/net/imap.rb, line 3263
def record_untagged_response_code(resp)
  return unless resp.data.is_a?(ResponseText)
  return unless (code = resp.data.code)
  @responses[code.name] << code.data
end
sasl_adapter() 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3562
def sasl_adapter
  SASLAdapter.new(self, &method(:send_command_with_continuations))
end
search_args(keys, charset_arg = nil, return: nil, charset: nil) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3367
def search_args(keys, charset_arg = nil, return: nil, charset: nil)
  {return:} => {return: return_kw}
  case [return_kw, keys]
  in [nil, Array[RETURN_WHOLE, return_opts, *keys]]
    return_opts = convert_return_opts(return_opts)
    esearch = true
  in [nil => return_opts, RETURN_START]
    esearch = true
  in [nil => return_opts, keys]
    esearch = false
  in [_, Array[RETURN_WHOLE, _, *] | RETURN_START]
    raise ArgumentError, "conflicting return options"
  in [_, Array[RETURN_WHOLE, _, *]] # workaround for https://bugs.ruby-lang.org/issues/20956
    raise ArgumentError, "conflicting return options"
  in [_, RETURN_START]              # workaround for https://bugs.ruby-lang.org/issues/20956
    raise ArgumentError, "conflicting return options"
  in [return_opts, keys]
    return_opts = convert_return_opts(return_opts)
    esearch = true
  end
  if charset && charset_arg
    raise ArgumentError, "multiple charset arguments"
  end
  charset ||= charset_arg
  # NOTE: not handling combined RETURN and CHARSET for raw strings
  if charset && keys in /\ACHARSET\b/i | Array[/\ACHARSET\z/i, *]
    raise ArgumentError, "multiple charset arguments"
  end
  args = normalize_searching_criteria(keys)
  args.prepend("CHARSET", charset)     if charset
  args.prepend("RETURN",  return_opts) if return_opts
  return args, esearch
end
search_internal(cmd, ...) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3416
def search_internal(cmd, ...)
  args, esearch = search_args(...)
  synchronize do
    tagged = send_command(cmd, *args)
    tag    = tagged.tag
    # Only the last ESEARCH or SEARCH is used.  Excess results are ignored.
    esearch_result = extract_responses("ESEARCH") {|response|
      response in ESearchResult(tag: ^tag)
    }.last
    search_result = clear_responses("SEARCH").last
    if esearch_result
      # silently ignore SEARCH results, if any
      esearch_result
    elsif search_result
      # warn EXPECTED_ESEARCH_RESULT if esearch
      search_result
    elsif esearch
      # warn NO_SEARCH_RESPONSE
      ESearchResult[tag:, uid: cmd.start_with?("UID ")]
    else
      # warn NO_SEARCH_RESPONSE
      SearchResult[]
    end
  end
end
send_command(cmd, *args, &block) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3291
def send_command(cmd, *args, &block)
  synchronize do
    args.each do |i|
      validate_data(i)
    end
    tag = generate_tag
    put_string(tag + " " + cmd)
    args.each do |i|
      put_string(" ")
      send_data(i, tag)
    end
    put_string(CRLF)
    if cmd == "LOGOUT"
      @logout_command_tag = tag
    end
    if block
      add_response_handler(&block)
    end
    begin
      return get_tagged_response(tag, cmd)
    ensure
      if block
        remove_response_handler(block)
      end
    end
  end
end
send_command_with_continuations(cmd, *args) { |data.text| ... } 点击切换源代码

调用 send_command,产生每个 ContinuationRequest 的文本,并使用每个块结果进行响应。返回 TaggedResponse。引发 NoResponseErrorBadResponseError

# File net-imap-0.5.4/lib/net/imap.rb, line 3282
def send_command_with_continuations(cmd, *args)
  send_command(cmd, *args) do |server_response|
    if server_response.instance_of?(ContinuationRequest)
      client_response = yield server_response.data.text
      put_string(client_response + CRLF)
    end
  end
end
send_data(data, tag = nil) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap/command_data.rb, line 34
def send_data(data, tag = nil)
  case data
  when nil
    put_string("NIL")
  when String
    send_string_data(data, tag)
  when Integer
    send_number_data(data)
  when Array
    send_list_data(data, tag)
  when Time, DateTime
    send_time_data(data)
  when Date
    send_date_data(data)
  when Symbol
    send_symbol_data(data)
  else
    data.send_data(self, tag)
  end
end
send_date_data(date) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap/command_data.rb, line 116
def send_date_data(date) put_string Net::IMAP.encode_date(date) end
send_list_data(list, tag = nil) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap/command_data.rb, line 102
def send_list_data(list, tag = nil)
  put_string("(")
  first = true
  list.each do |i|
    if first
      first = false
    else
      put_string(" ")
    end
    send_data(i, tag)
  end
  put_string(")")
end
send_literal(str, tag = nil) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap/command_data.rb, line 81
def send_literal(str, tag = nil)
  synchronize do
    put_string("{" + str.bytesize.to_s + "}" + CRLF)
    @continued_command_tag = tag
    @continuation_request_exception = nil
    begin
      @continuation_request_arrival.wait
      e = @continuation_request_exception || @exception
      raise e if e
      put_string(str)
    ensure
      @continued_command_tag = nil
      @continuation_request_exception = nil
    end
  end
end
send_number_data(num) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap/command_data.rb, line 98
def send_number_data(num)
  put_string(num.to_s)
end
send_quoted_string(str) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap/command_data.rb, line 77
def send_quoted_string(str)
  put_string('"' + str.gsub(/["\\]/, "\\\\\\&") + '"')
end
send_string_data(str, tag = nil) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap/command_data.rb, line 55
def send_string_data(str, tag = nil)
  if str.empty?
    put_string('""')
  elsif str.match?(/[\r\n]/n)
    # literal, because multiline
    send_literal(str, tag)
  elsif !str.ascii_only?
    if @utf8_strings
      # quoted string
      send_quoted_string(str)
    else
      # literal, because of non-ASCII bytes
      send_literal(str, tag)
    end
  elsif str.match?(/[(){ \x00-\x1f\x7f%*"\\]/n)
    # quoted string
    send_quoted_string(str)
  else
    put_string(str)
  end
end
send_symbol_data(symbol) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap/command_data.rb, line 119
def send_symbol_data(symbol)
  put_string("\\" + symbol.to_s)
end
send_time_data(time) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap/command_data.rb, line 117
def send_time_data(time) put_string Net::IMAP.encode_time(time) end
sort_internal(cmd, sort_keys, search_keys, charset) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3488
def sort_internal(cmd, sort_keys, search_keys, charset)
  search_keys = normalize_searching_criteria(search_keys)
  synchronize do
    send_command(cmd, sort_keys, charset, *search_keys)
    clear_responses("SORT").last || []
  end
end
start_imap_connection() 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3103
def start_imap_connection
  @greeting        = get_server_greeting
  @capabilities    = capabilities_from_resp_code @greeting
  @receiver_thread = start_receiver_thread
rescue Exception
  @sock.close
  raise
end
start_receiver_thread() 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3120
def start_receiver_thread
  Thread.start do
    receive_responses
  rescue Exception => ex
    @receiver_thread_exception = ex
    # don't exit the thread with an exception
  end
end
start_tls_session() 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3548
def start_tls_session
  raise "SSL extension not installed" unless defined?(OpenSSL::SSL)
  raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
  raise "cannot start TLS without SSLContext" unless ssl_ctx
  @sock = SSLSocket.new(@sock, ssl_ctx)
  @sock.sync_close = true
  @sock.hostname = @host if @sock.respond_to? :hostname=
  ssl_socket_connect(@sock, open_timeout)
  if ssl_ctx.verify_mode != VERIFY_NONE
    @sock.post_connection_check(@host)
    @tls_verified = true
  end
end
store_internal(cmd, set, attr, flags, unchangedsince: nil) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3472
def store_internal(cmd, set, attr, flags, unchangedsince: nil)
  attr = RawData.new(attr) if attr.instance_of?(String)
  args = [SequenceSet.new(set)]
  args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
  args << attr << flags
  synchronize do
    clear_responses("FETCH")
    send_command(cmd, *args)
    clear_responses("FETCH")
  end
end
tcp_socket(host, port) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3129
def tcp_socket(host, port)
  s = Socket.tcp(host, port, :connect_timeout => open_timeout)
  s.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, true)
  s
rescue Errno::ETIMEDOUT
  raise Net::OpenTimeout, "Timeout to open TCP connection to " +
    "#{host}:#{port} (exceeds #{open_timeout} seconds)"
end
thread_internal(cmd, algorithm, search_keys, charset) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap.rb, line 3496
def thread_internal(cmd, algorithm, search_keys, charset)
  search_keys = normalize_searching_criteria(search_keys)
  synchronize do
    send_command(cmd, algorithm, charset, *search_keys)
    clear_responses("THREAD").last || []
  end
end
validate_data(data) 点击切换源代码
# File net-imap-0.5.4/lib/net/imap/command_data.rb, line 13
def validate_data(data)
  case data
  when nil
  when String
  when Integer
    NumValidator.ensure_number(data)
  when Array
    if data[0] == 'CHANGEDSINCE'
      NumValidator.ensure_mod_sequence_value(data[1])
    else
      data.each do |i|
        validate_data(i)
      end
    end
  when Time, Date, DateTime
  when Symbol
  else
    data.validate
  end
end

基本邮箱属性

↑ 返回顶部

常量

HASCHILDREN

HAS_CHILDREN 的别名,与 IMAP 拼写匹配。

HASNOCHILDREN

HAS_NO_CHILDREN 的别名,与 IMAP 拼写匹配。

HAS_CHILDREN

此属性的存在表示邮箱具有子邮箱。如果存在子邮箱且用户没有权限访问任何子邮箱,则服务器不应设置此属性。在这种情况下,应该使用 \HasNoChildren。 然而,在许多情况下,服务器可能无法有效地计算用户是否有权访问任何子邮箱。请注意,即使邮箱的 \HasChildren 属性在处理邮箱时必须正确,客户端也必须准备好处理邮箱标记为 \HasChildren 属性的情况,但在对 list 命令的响应中没有出现子邮箱。例如,这可能是由于子邮箱被删除或在服务器能够列出它们之前被另一个客户端(使用访问控制)设置为用户无法访问。

服务器在同一个 list 响应中同时返回 \HasChildren\HasNoChildren 属性是错误的。遇到同时存在 \HasChildren\HasNoChildren 属性的 list 响应的客户端应视为 list 响应中都缺失这两个属性。

HAS_NO_CHILDREN

此属性的存在表示邮箱没有当前经过身份验证的用户可以访问的子邮箱。

服务器在同一个 list 响应中同时返回 \HasChildren\HasNoChildren 属性是错误的。遇到同时存在 \HasChildren\HasNoChildren 属性的 list 响应的客户端应视为 list 响应中都缺失这两个属性。

注意:\HasNoChildren 属性不应与 \NoInferiors 属性混淆,后者表示现在不存在子邮箱,并且将来也无法创建子邮箱。

MARKED

邮箱已被服务器标记为“感兴趣”;邮箱可能包含自上次选择邮箱以来添加的消息。

如果服务器无法确定邮箱是否“有趣”,则服务器不应发送 \Marked\Unmarked。对于单个邮箱,服务器不得发送超过一个 \Marked\Unmarked\NoSelect,并且可以不发送任何一个。

NOINFERIORS

NO_INFERIORS 的别名,与 IMAP 拼写匹配。

NONEXISTENT

\NonExistent 属性表示邮箱名称不指向现有邮箱。请注意,此属性本身没有意义,因为与规范 list 模式匹配但不存在的邮箱名称,除非满足下面列出的两个条件之一,否则不得返回。

  1. 邮箱名称也满足选择标准(例如,它已订阅并且已指定“SUBSCRIBED”选择选项)。

  2. 已指定“RECURSIVEMATCH”,并且邮箱名称至少有一个不匹配 list 模式且确实匹配选择标准的后代邮箱名称。

实际上,这意味着 \NonExistent 属性通常与 \Subscribed\Remote\HasChildren 或 CHILDINFO 扩展数据项中的一个或多个一起返回。

客户端必须将 \NonExistent 属性的存在视为服务器也发送了 \NoSelect 属性

NOSELECT

NO_SELECT 的别名,与 IMAP 拼写匹配。

NO_INFERIORS

邮箱属性,表示此名称下不可能存在任何子层级;现在不存在子层级,并且将来也无法创建子层级。

客户端必须将 \NoInferiors 属性的存在视为服务器也发送了 \HasNoChildren 属性

NO_SELECT

邮箱属性,表示不可能将此名称用作可选择的邮箱。

REMOTE

邮箱是一个远程邮箱。

SUBSCRIBED

邮箱名称是使用 subscribe 命令订阅的。

UNMARKED

自上次选择邮箱以来,该邮箱不包含任何其他消息。

如果服务器无法确定邮箱是否“有趣”,则服务器不应发送 \Marked\Unmarked。对于单个邮箱,服务器不得发送超过一个 \Marked\Unmarked\NoSelect,并且可以不发送任何一个。

邮箱角色属性

↑ 返回顶部

常量

ALL

邮箱属性,表示此邮箱显示用户消息存储中的所有消息。实现可以省略某些消息,例如垃圾箱和垃圾邮件中的消息。当支持此特殊用途时,几乎可以肯定它代表一个虚拟邮箱。

ARCHIVE

邮箱属性,表示此邮箱用于存档消息。“存档”邮箱的含义取决于服务器;通常,它将用于将消息移出收件箱,或者以其他方式使其远离用户,同时仍使其可访问。

DRAFTS

邮箱属性,表示此邮箱用于保存草稿消息——通常是正在撰写但尚未发送的消息。在某些服务器实现中,这可能是一个虚拟邮箱,其中包含来自其他邮箱的标记为“草稿”消息标志的消息。或者,这可能只是建议客户端将草稿放在此处。

JUNK

邮箱属性,表示此邮箱是存放被视为垃圾邮件的消息的位置。一些服务器实现可能会自动将消息放在此处。或者,这可能只是对客户端垃圾邮件过滤器的建议。

SENT

邮箱属性,表示此邮箱用于保存已发送消息的副本。一些服务器实现可能会自动将消息放在此处。或者,这可能只是建议客户端将已发送消息保存在此处。

TRASH

邮箱属性,表示此邮箱用于保存已删除或标记为删除的消息。在某些服务器实现中,这可能是一个虚拟邮箱,其中包含来自其他邮箱的标记为 \Deleted 消息标志的消息。或者,这可能只是建议选择不使用 IMAP \Deleted 模型的客户端应将其用作垃圾箱位置。在严格期望 IMAP \Deleted 模型的服务器实现中,可能不支持此特殊用途。

系统标志

↑ 返回顶部

常量

ANSWERED

表示消息已被回复的标志。

DELETED

表示消息已标记为删除的标志。这将在邮箱关闭或清除时发生。

DRAFT

表示消息只是草稿或正在进行中的版本的标志。

FLAGGED

表示消息已被标记为特殊或紧急注意的消息标志。

也是一个邮箱特殊用途属性,表示此邮箱显示所有以某种方式标记为“重要”的消息。当支持此特殊用途时,它很可能代表一个虚拟邮箱,收集(来自其他邮箱的)标记为“已标记”消息标志的消息。

RECENT

表示消息是“最近”的标志,这意味着此会话是客户端第一次收到此消息通知的会话。

此标志由 IMAP4rev1 定义,并已由 IMAP4rev2 弃用。

SEEN

表示消息已被阅读的标志。