"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)

   This class implements the actual IMAP4 protocol.  The connection is
   created and protocol version (IMAP4 or IMAP4rev1) is determined
   when the instance is initialized. If *host* is not specified, "''"
   (the local host) is used. If *port* is omitted, the standard IMAP4
   port (143) is used.

   "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" 语句的支持。

有三个异常被定义为 "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)

   这是一个派生自 "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"。

   在 3.3 版更改: *ssl_context* parameter added.

   在 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 证书。

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

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" 例外，而
"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)

   Opens socket to *port* at *host*.  This method is implicitly called
   by the "IMAP4" constructor.  The connection objects established by
   this method will be used in the "IMAP4.read()", "IMAP4.readline()",
   "IMAP4.send()", and "IMAP4.shutdown()" methods.  You may override
   this method.

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

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" 的变化形式，带有结果排序语句。 返回的数据包
   含以空格分隔的匹配消息编号列表。

   sort 命令在 *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.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()
