"imaplib" --- IMAP4 协议客户端
******************************

**源代码：** Lib/imaplib.py

======================================================================

本模块定义了三个类： "IMAP4" 、 "IMAP4_SSL" 和 "IMAP4_stream" 。这三个
类封装了与IMAP4服务器的连接并实现了 **RFC 2060** 当中定义的大多数
IMAP4rev1客户端协议。其与IMAP4（ **RFC 1730** ）服务器后向兼容，但是
"STATUS"  指令在IMAP4中不支持。

"imaplib" 模块提供了三个类，其中 "IMAP4" 是基类：

class imaplib.IMAP4(host='', port=IMAP4_PORT, timeout=None)

   这个类实现了实际的 IMAP4 协议。 当其实例被实例化时会创建连接并确定
   协议版本 (IMAP4 或 IMAP4rev1)。 如果未指明 *host*，则会使用 "''" (
   本地主机)。 如果省略 *port*，则会使用标准 IMAP4 端口 (143)。 可选的
   *timeout* 形参指定连接尝试的超时秒数。 如果未指定超时或为 None，则
   会使用全局默认的套接字超时。

   "IMAP4" 类支持 "with" 语句。 当这样使用时，IMAP4 "LOGOUT" 命令会在
   "with" 语句退出时自动发出。 例如:

      >>> from imaplib import IMAP4
      >>> with IMAP4("domain.org") as M:
      ...     M.noop()
      ...
      ('OK', [b'Nothing Accomplished. d25if65hy903weo.87'])

   在 3.5 版更改: 支持了 "with" 语句。

   在 3.9 版更改: 添加了可选的 *timeout* 形参。

有三个异常被定义为 "IMAP4" 类的属性:

exception IMAP4.error

   任何错误都将引发该异常。 异常的原因会以字符串的形式传递给构造器。

exception IMAP4.abort

   IMAP4 服务器错误会导致引发该异常。 这是 "IMAP4.error" 的子类。 请注
   意关闭此实例并实例化一个新实例通常将会允许从该异常中恢复。

exception IMAP4.readonly

   当一个可写邮箱的状态被服务器修改时会引发此异常。  此异常是
   "IMAP4.error" 的子类。 某个其他客户端现在会具有写入权限，将需要重新
   打开该邮箱以重新获得写入权限。

另外还有一个针对安全连接的子类:

class imaplib.IMAP4_SSL(host='', port=IMAP4_SSL_PORT, keyfile=None, certfile=None, ssl_context=None, timeout=None)

   这是一个派生自 "IMAP4" 的子类，它使用经 SSL 加密的套接字进行连接 (
   为了使用这个类你需要编译时附带 SSL 支持的 socket 模块)。 如果未指定
   *host*，则会使用 "''" (本地主机)。 如果省略了 *port*，则会使用标准
   的 IMAP4-over-SSL 端口 (993)。 *ssl_context* 是一个
   "ssl.SSLContext" 对象，它允许将 SSL 配置选项、证书和私钥打包放入一
   个单独的 (可以长久存在的) 结构体中。 请阅读 安全考量 以获取最佳实践
   。

   *keyfile* 和 *certfile* 是 *ssl_context* 的旧式替代品 —— 它们可以指
   向 PEM 格式的私钥和证书链文件用于 SSL 连接。 请注意
   *keyfile*/*certfile* 形参不能与 *ssl_context* 共存，如果
   *keyfile*/*certfile* 与 *ssl_context* 一同被提供则会引发
   "ValueError"。

   可选的 *timeout* 形参指明连接尝试的超时秒数。 如果参数未给出或为
   None，则会使用全局默认的套接字超时设置。

   在 3.3 版更改: 增加了 *ssl_context* 形参。

   在 3.4 版更改: 本类现在支持通过 "ssl.SSLContext.check_hostname" 和
   *服务器名称提示* （参阅 "ssl.HAS_SNI"）进行主机名检查。

   3.6 版后已移除: *keyfile* 和 *certfile* 已弃用并转而推荐
   *ssl_context*。 请改用 "ssl.SSLContext.load_cert_chain()"，或让
   "ssl.create_default_context()" 为你选择系统所信任的 CA 证书。

   在 3.9 版更改: 添加了可选的 *timeout* 形参。

第二个子类允许由子进程所创建的连接:

class imaplib.IMAP4_stream(command)

   这是一个派生自 "IMAP4" 的子类，它可以连接 "stdin/stdout" 文件描述符
   ，此种文件是通过向 "subprocess.Popen()" 传入 *command* 来创建的。

定义了下列工具函数:

imaplib.Internaldate2tuple(datestr)

   解析一个 IMAP4 "INTERNALDATE" 字符串并返回对应的本地时间。 返回值是
   一个 "time.struct_time" 元组或者如果字符串格式错误则为 "None"。

imaplib.Int2AP(num)

   将一个整数转换为使用字符集 ["A" .. "P"] 的字节串表示形式。

imaplib.ParseFlags(flagstr)

   将一个 IMAP4 "FLAGS" 响应转换为包含单独旗标的元组。

imaplib.Time2Internaldate(date_time)

   将 *date_time* 转换为 IMAP4 "INTERNALDATE" 表示形式。 返回值是以下
   形式的字符串: ""DD-Mmm-YYYY HH:MM:SS +HHMM"" (包括双引号)。
   *date_time* 参数可以是一个代表距离纪元起始的秒数 (如 "time.time()"
   的返回值) 的数字 (整数或浮点数)，一个代表本地时间的 9 元组，一个
   "time.struct_time" 实例 (如 "time.localtime()" 的返回值)，一个感知
   型的 "datetime.datetime" 实例，或一个双引号字符串。 在最后一种情况
   下，它会被假定已经具有正确的格式。

请注意 IMAP4 消息编号会随邮箱的改变而改变；特别是在使用 "EXPUNGE" 命令
执行删除后剩余的消息会被重新编号。 因此高度建议通过 UID 命令来改用 UID
。

模块的最后有一段测试，其中包含的用法示例更加广泛。

参见:

  描述该协议的文档，实现该协议的服务器源代码，由华盛顿大学 IMAP 信息中
  心提供 (**源代码**) https://github.com/uw-imap/imap (**不再维护**)。


IMAP4 对象
==========

所有 IMAP4rev1 命令都表示为同名的方法，可以为大写或小写形式。

命令的所有参数都会被转换为字符串，只有 "AUTHENTICATE" 例外，而, and
the last argument to "APPEND" 的最后一个参数会被作为 IMAP4 字面值传入
。 如有必要 (字符串包含 IMAP4 协议中的敏感字符并且未加圆括号或双引号)
每个字符串都会被转码。 但是，"LOGIN" 命令的 *password* 参数总是会被转
码。 如果你想让某个参数字符串免于被转码 (例如: "STORE" 的 *flags* 参数
) 则要为该字符串加上圆括号 (例如: "r'(\Deleted)'")。

每条命令均返回一个元组: "(type, [data, ...])" 其中 *type* 通常为
"'OK'" 或 "'NO'"，而 *data* 为来自命令响应的文本，或为来自命令的规定结
果。 每个 *data* 均为 "bytes" 或者元组。 如果为元组，则其第一部分是响
应的标头，而第二部分将包含数据 (例如: 'literal' 值)。

以下命令的 *message_set* 选项为指定要操作的一条或多条消息的字符串。 它
可以是一个简单的消息编号 ("'1'")，一段消息编号区间 ("'2:4'")，或者一组
以逗号分隔的非连续区间 ("'1:3,6:9'")。 区间可以包含一个星号来表示无限
的上界 ("'3:*'")。

"IMAP4" 实例具有下列方法:

IMAP4.append(mailbox, flags, date_time, message)

   将 *message* 添加到指定的邮箱。

IMAP4.authenticate(mechanism, authobject)

   认证命令 --- 要求对响应进行处理。

   *mechanism* 指明要使用哪种认证机制 —— 它应当在实例变量
   "capabilities" 中以 "AUTH=mechanism" 的形式出现。

   *authobject* 必须是一个可调用对象:

      data = authobject(response)

   它将被调用以便处理服务器连续响应；传给它的 *response* 参数将为
   "bytes" 类型。 它应当返回 base64 编码的 "bytes" *数据* 并发送给服务
   器。 或者在客户端中止响应时返回 "None" 并应改为发送 "*"。

   在 3.5 版更改: 字符串形式的用户名和密码现在会被执行 "utf-8" 编码而
   不限于 ASCII 字符。

IMAP4.check()

   为服务器上的邮箱设置检查点。

IMAP4.close()

   关闭当前选定的邮箱。 已删除的消息会从可写邮箱中被移除。 在 "LOGOUT"
   之前建议执行此命令。

IMAP4.copy(message_set, new_mailbox)

   将 *message_set* 消息拷贝到 *new_mailbox* 的末尾。

IMAP4.create(mailbox)

   新建名为 *mailbox* 新邮箱。

IMAP4.delete(mailbox)

   删除名为 *mailbox* 的旧邮箱。

IMAP4.deleteacl(mailbox, who)

   删除邮箱上某人的 ACL (移除任何权限)。

IMAP4.enable(capability)

   启用 *capability* (参见 **RFC 5161**)。 大多数功能都不需要被启用。
   目前只有 "UTF8=ACCEPT" 功能受到支持 (参见 **RFC 6855**)。

   3.5 新版功能: "enable()" 方法本身，以及 **RFC 6855** 支持。

IMAP4.expunge()

   从选定的邮箱中永久移除被删除的条目。 为每条被删除的消息各生成一个
   "EXPUNGE" 响应。 返回包含按接收时间排序的 "EXPUNGE" 消息编号的列表
   。

IMAP4.fetch(message_set, message_parts)

   获取消息（的各个部分）。 *message_parts* 应为加圆标号的消息部分名称
   字符串，例如: ""(UID BODY[TEXT])""。 返回的数据是由消息部分封包和数
   据组成的元组。

IMAP4.getacl(mailbox)

   获取 *mailbox* 的 "ACL"。 此方法是非标准的，但是被 "Cyrus" 服务器所
   支持。

IMAP4.getannotation(mailbox, entry, attribute)

   提取 *mailbox* 的特定 "ANNOTATION"。 此方法是非标准的，但是被
   "Cyrus" 服务器所支持。

IMAP4.getquota(root)

   获取 "quota" *root* 的资源使用和限制。 此方法是 rfc2087 定义的
   IMAP4 QUOTA 扩展的组成部分。

IMAP4.getquotaroot(mailbox)

   获取指定 *mailbox* 的 "quota" "roots" 列表。 此方法是 rfc2087 定义
   的 IMAP4 QUOTA 扩展的组成部分。

IMAP4.list([directory[, pattern]])

   列出 *directory* 中与 *pattern* 相匹配的邮箱名称。 *directory* 默认
   为最高层级的电邮文件夹，而 *pattern* 默认为匹配任何文本。 返回的数
   据包含 "LIST" 响应列表。

IMAP4.login(user, password)

   使用纯文本密码标识客户。 *password* 将被转码。

IMAP4.login_cram_md5(user, password)

   在标识用户以保护密码时强制使用 "CRAM-MD5" 认证。 将只在服务器
   "CAPABILITY" 响应包含 "AUTH=CRAM-MD5" 阶段时才有效。

IMAP4.logout()

   关闭对服务器的连接。 返回服务器 "BYE" 响应。

   在 3.8 版更改: 此方法不会再忽略静默的任意异常。

IMAP4.lsub(directory='""', pattern='*')

   列出 directory 中抽取的与 pattern 相匹配的邮箱。 *directory* 默认为
   最高层级目录而 *pattern* 默认为匹配任何邮箱。 返回的数据为消息部分
   封包和数据的元组。

IMAP4.myrights(mailbox)

   显示某个邮箱的本人 ACL (即本人在邮箱中的权限)。

IMAP4.namespace()

   返回 **RFC 2342** 中定义的 IMAP 命名空间。

IMAP4.noop()

   将 "NOOP" 发送给服务器。

IMAP4.open(host, port, timeout=None)

   打开连接 *host* 上 *port* 的套接字。 可选的 *timeout* 形参指定连接
   尝试的超时秒数。 如果 timeout 未给出或为 None，则会使用全局默认的套
   接字超时。 另外请注意如果 *timeout* 形参被设为零，它将引发
   "ValueError" 以拒绝创建非阻塞套接字。 此方法会由 "IMAP4" 构造器隐式
   地调用。 此方法所建立的连接对象将在 "IMAP4.read()",
   "IMAP4.readline()", "IMAP4.send()" 和 "IMAP4.shutdown()" 等方法中被
   使用。 你可以重载此方法。

   引发一个 审计事件 "imaplib.open"，附带参数 "self", "host", "port"。

   在 3.9 版更改: 加入 *timeout* 参数。

IMAP4.partial(message_num, message_part, start, length)

   获取消息被截断的部分。 返回的数据是由消息部分封包和数据组成的元组。

IMAP4.proxyauth(user)

   作为 *user* 进行认证。 允许经权限的管理员通过代理进入任意用户的邮箱
   。

IMAP4.read(size)

   从远程服务器读取 *size* 字节。 你可以重载此方法。

IMAP4.readline()

   从远程服务器读取一行。 你可以重载此方法。

IMAP4.recent()

   提示服务器进行更新。 如果没有新消息则返回的数据为 "None"，否则为
   "RECENT" 响应的值。

IMAP4.rename(oldmailbox, newmailbox)

   将名为 *oldmailbox* 的邮箱重命名为 *newmailbox*。

IMAP4.response(code)

   如果收到响应 *code* 则返回其数据，否则返回 "None"。 返回给定的代码
   ，而不是普通的类型。

IMAP4.search(charset, criterion[, ...])

   在邮箱中搜索匹配的消息。 *charset* 可以为 "None"，在这种情况下在发
   给服务器的请求中将不指定 "CHARSET"。 IMAP 协议要求至少指定一个标准
   ；当服务器返回错误时将会引发异常。 *charset* 为 "None" 对应使用
   "enable()" 命令启用了 "UTF8=ACCEPT" 功能的情况。

   示例:

      # M is a connected IMAP4 instance...
      typ, msgnums = M.search(None, 'FROM', '"LDJ"')

      # or:
      typ, msgnums = M.search(None, '(FROM "LDJ")')

IMAP4.select(mailbox='INBOX', readonly=False)

   选择一个邮箱。 返回的数据是 *mailbox* 中消息的数量 ("EXISTS" 响应)
   。 默认的 *mailbox* 为 "'INBOX'"。 如果设置了 *readonly* 旗标，则不
   允许修改该邮箱。

IMAP4.send(data)

   将 "data" 发送给远程服务器。 请可以重载此方法。

   引发一个 审计事件 "imaplib.send"，附带参数 "self", "data"。

IMAP4.setacl(mailbox, who, what)

   发送 *mailbox* 的 "ACL"。 此方法是非标准的，但是被 "Cyrus" 服务器所
   支持。

IMAP4.setannotation(mailbox, entry, attribute[, ...])

   设置 *mailbox* 的 "ANNOTATION"。 此方法是非标准的，但是被 "Cyrus"
   服务器所支持。

IMAP4.setquota(root, limits)

   设置 "quota" *root* 的资源限制为 *limits*。 此方法是 rfc2087 定义的
   IMAP4 QUOTA 扩展的组成部分。

IMAP4.shutdown()

   关闭在 "open" 中建立的连接。 此方法会由 "IMAP4.logout()" 隐式地调用
   。 你可以重载此方法。

IMAP4.socket()

   返回用于连接服务器的套接字实例。

IMAP4.sort(sort_criteria, charset, search_criterion[, ...])

   "sort" 命令是 "search" 的变化形式，带有结果排序语句。 返回的数据包
   含以空格分隔的匹配消息编号列表。

   排序命令在 *search_criterion* 参数之前还有两个参数；一个带圆括号的
   *sort_criteria* 列表，和搜索的 *charset*。 请注意不同于 "search"，
   搜索的 *charset* 参数是强制性的。 还有一个 "uid sort" 命令与 "sort"
   对应，如同 "uid search" 与 "search" 对应一样。 "sort" 命令首先在邮
   箱中搜索匹配给定搜索条件的消息，使用 charset 参数来解读搜索条件中的
   字符串。 然后它将返回所匹配消息的编号。

   这是一个 "IMAP4rev1" 扩展命令。

IMAP4.starttls(ssl_context=None)

   发送一个 "STARTTLS" 命令。 *ssl_context* 参数是可选的并且应为一个
   "ssl.SSLContext" 对象。 这将在 IMAP 连接上启用加密。 请阅读 安全考
   量 来了解最佳实践。

   3.2 新版功能.

   在 3.4 版更改: 此方法现在支持使用 "ssl.SSLContext.check_hostname"
   和 *服务器名称指示* (参见 "ssl.HAS_SNI") 进行主机名检查。

IMAP4.status(mailbox, names)

   针对 *mailbox* 请求指定的状态条件。

IMAP4.store(message_set, command, flag_list)

   改变邮箱中消息的旗标处理。 *command* 由 **RFC 2060** 的 6.4.6 小节
   指明，应为 "FLAGS", "+FLAGS" 或 "-FLAGS" 之一，并可选择附带
   ".SILENT" 后缀。

   例如，要在所有消息上设置删除旗标:

      typ, data = M.search(None, 'ALL')
      for num in data[0].split():
         M.store(num, '+FLAGS', '\\Deleted')
      M.expunge()

   注解:

     创建包含 ']' 的旗标 (例如: "[test]") 会违反 **RFC 3501** (IMAP 协
     议)。 但是，imaplib 在历史上曾经允许创建这样的标签，并且流行的
     IMAP 服务器如 Gmail 都会接受并生成这样的旗标。 有些非 Python 程序
     也会创建这样的旗标。 虽然它违反 RFC 并且 IMAP 客户端和服务器应当
     严格遵守规范，但是 imaplib 出于向下兼容的理由仍然继续允许创建这样
     的标签，并且在 Python 3.6 中会在其被服务器所发送时处理它们，因为
     这能提升实际的兼容性。

IMAP4.subscribe(mailbox)

   订阅新邮箱。

IMAP4.thread(threading_algorithm, charset, search_criterion[, ...])

   "thread" 命令是 "search" 的变化形式，带有针对结果的消息串句法。 返
   回的数据包含以空格分隔的消息串成员列表。

   消息串成员由零个或多个消息编号组成，以空格分隔，标示了连续的上下级
   关系。

   thread 命令在 *search_criterion* 参数之前还有两个参数；一个
   *threading_algorithm*，以及搜索使用的 *charset*。 请注意不同于
   "search"，搜索使用的 *charset* 参数是强制性的。 还有一个 "uid
   thread" 命令与 "thread" 对应，如同 "uid search" 与 "search" 对应一
   个。 "thread" 命令首先在邮箱中搜索匹配给定搜索条件的消息，使用
   charset 参数来解读搜索条件中的字符串。 然后它将按照指定的消息串算法
   返回所匹配的消息串。

   这是一个 "IMAP4rev1" 扩展命令。

IMAP4.uid(command, arg[, ...])

   执行 command arg 并附带用 UID 所标识的消息，而不是用消息编号。 返回
   与命令对应的响应。 必须至少提供一个参数；如果不提供任何参数，服务器
   将返回错误并引发异常。

IMAP4.unsubscribe(mailbox)

   取消订阅原有邮箱。

IMAP4.unselect()

   "imaplib.IMAP4.unselect()" 会释放关联到选定邮箱的服务器资源并将服务
   器返回到已认证状态。 此命令会执行与 "imaplib.IMAP4.close()" 相同的
   动作，区别在于它不会从当前选定邮箱中永久性地移除消息。

   3.9 新版功能.

IMAP4.xatom(name[, ...])

   允许服务器在 "CAPABILITY" 响应中通知简单的扩展命令。

在 "IMAP4" 的实例上定义了下列属性:

IMAP4.PROTOCOL_VERSION

   在服务器的 "CAPABILITY" 响应中最新的受支持协议。

IMAP4.debug

   控制调试输出的整数值。 初始值会从模块变量 "Debug" 中获取。 大于三的
   值表示将追踪每一条命令。

IMAP4.utf8_enabled

   通常为 "False" 的布尔值，但也可以被设为 "True"，如果成功地为
   "UTF8=ACCEPT" 功能发送了 "enable()" 命令的话。

   3.5 新版功能.


IMAP4 示例
==========

以下是一个最短示例（不带错误检查），该示例将打开邮箱，检索并打印所有消
息:

   import getpass, imaplib

   M = imaplib.IMAP4()
   M.login(getpass.getuser(), getpass.getpass())
   M.select()
   typ, data = M.search(None, 'ALL')
   for num in data[0].split():
       typ, data = M.fetch(num, '(RFC822)')
       print('Message %s\n%s\n' % (num, data[0][1]))
   M.close()
   M.logout()
