"smtplib" --- SMTP 協定用戶端
*****************************

**原始碼：**Lib/smtplib.py

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

"smtplib" 模組定義了一個 SMTP 用戶端 session 物件，可用於將郵件傳送至
任何具備 SMTP 或 ESMTP listener daemon 的網際網路機器。有關 SMTP 和
ESMTP 運作的細節，請參閱 **RFC 821**（Simple Mail Transfer Protocol，
簡易郵件傳輸協定）和 **RFC 1869**（SMTP Service Extensions，SMTP 服務
擴充）。

可用性: not WASI.

此模組在 WebAssembly 平台上不起作用或無法使用。更多資訊請參閱
WebAssembly 平台。

class smtplib.SMTP(host='', port=0, local_hostname=None, [timeout, ]source_address=None)

   An "SMTP" instance encapsulates an SMTP connection.  It has methods
   that support a full repertoire of SMTP and ESMTP operations.

   If the host parameter is set to a truthy value, "SMTP.connect()" is
   called with host and port automatically when the object is created;
   otherwise, "connect()" must be called manually.

   If specified, *local_hostname* is used as the FQDN of the local
   host in the HELO/EHLO command.  Otherwise, the local hostname is
   found using "socket.getfqdn()".  If the "connect()" call returns
   anything other than a success code, an "SMTPConnectError" is
   raised. The optional *timeout* parameter specifies a timeout in
   seconds for blocking operations like the connection attempt (if not
   specified, the global default timeout setting will be used).  If
   the timeout expires, "TimeoutError" is raised.  The optional
   *source_address* parameter allows binding to some specific source
   address in a machine with multiple network interfaces, and/or to
   some specific source TCP port. It takes a 2-tuple "(host, port)",
   for the socket to bind to as its source address before connecting.
   If omitted (or if *host* or *port* are "''" and/or "0"
   respectively) the OS default behavior will be used.

   一般使用上，你應該只會需要初始化/連線、"sendmail()" 和
   "SMTP.quit()" 等方法。以下附上一個範例。

   "SMTP" 類別支援 "with" 陳述式。當這樣使用時，在 "with" 陳述式結束時
   會自動發出 SMTP "QUIT" 指令。例如：

      >>> from smtplib import SMTP
      >>> with SMTP("domain.org") as smtp:
      ...     smtp.noop()
      ...
      (250, b'Ok')
      >>>

   所有指令都會引發一個附帶引數 "self"、"data" 的稽核事件
   "smtplib.SMTP.send"，其中 "data" 為即將傳送至遠端的位元組。

   default_port

      The default port used for SMTP connections (25).

   在 3.3 版的變更: 新增對 "with" 陳述式的支援。

   在 3.3 版的變更: 新增 source_address 引數。

   在 3.5 版被加入: 現在支援 SMTPUTF8 擴充（**RFC 6531**）。

   在 3.9 版的變更: 如果 *timeout* 參數被設為零，則會引發 "ValueError"
   ，以防止建立非阻塞的 socket。

class smtplib.SMTP_SSL(host='', port=0, local_hostname=None, *, [timeout, ]context=None, source_address=None)

   An "SMTP_SSL" instance behaves exactly the same as instances of
   "SMTP". "SMTP_SSL" should be used for situations where SSL is
   required from the beginning of the connection and using
   "SMTP.starttls()" is not appropriate.

   If the host parameter is set to a truthy value, "SMTP.connect()" is
   called with host and port automatically when the object is created;
   otherwise, "SMTP.connect()" must be called manually.

   The optional arguments *local_hostname*, *timeout* and
   *source_address* have the same meaning as they do in the "SMTP"
   class.  *context*, also optional, can contain a "SSLContext" and
   allows configuring various aspects of the secure connection.
   Please read Security considerations for best practices.

   default_port

      The default port used for SMTP-over-SSL connections (465).

   在 3.3 版的變更: 新增 *context*。

   在 3.3 版的變更: 新增 *source_address* 引數。

   在 3.4 版的變更: 此類別現在支援以 "ssl.SSLContext.check_hostname"
   進行主機名稱檢查以及 *伺服器名稱指示 (Server Name Indication)*（請
   參閱 "ssl.HAS_SNI"）。

   在 3.9 版的變更: 如果 *timeout* 參數被設為零，則會引發 "ValueError"
   ，以防止建立非阻塞的 socket

   在 3.12 版的變更: 已棄用的 *keyfile* 和 *certfile* 參數已被移除。

class smtplib.LMTP(host='', port=LMTP_PORT, local_hostname=None, source_address=None[, timeout])

   LMTP 協定與 ESMTP 非常相似，它大量基於標準的 SMTP 用戶端。LMTP 常會
   使用 Unix socket，因此我們的 "connect()" 方法除了支援一般的
   host:port 伺服器外，也必須支援它。選用引數 *local_hostname* 和
   *source_address* 的意義與它們在 "SMTP" 類別中的相同。要指定一個
   Unix socket，你必須為 *host* 使用以 '/' 開頭的絕對路徑。

   支援使用一般的 SMTP 機制進行身分驗證。當使用 Unix socket 時，LMTP
   通常不支援或不需要任何身分驗證，但實際情況可能因人而異。

   在 3.9 版的變更: 新增 *timeout* 選用參數。

此外也定義了一系列例外：

exception smtplib.SMTPException

   "OSError" 的子類別，是此模組所提供之所有其他例外的基底例外類別。

   在 3.4 版的變更: SMTPException 成為 "OSError" 的子類別

exception smtplib.SMTPServerDisconnected

   當伺服器意外斷線時，或者當嘗試在將 "SMTP" 實例連線至伺服器之前就使
   用它時，會引發此例外。

exception smtplib.SMTPResponseException

   所有包含 SMTP 錯誤碼的例外的基底類別。當 SMTP 伺服器回傳錯誤碼時，
   在某些情況下會產生這些例外。

   smtp_code

      錯誤碼。

   smtp_error

      錯誤訊息。

exception smtplib.SMTPSenderRefused

   寄件者位址被拒絕。除了所有 "SMTPResponseException" 例外所設定的屬性
   外，這還會將 'sender' 設定為 SMTP 伺服器所拒絕的字串。

exception smtplib.SMTPRecipientsRefused

   所有收件者位址都被拒絕。

   recipients

      一個 dictionary，其類型與 "SMTP.sendmail()" 所回傳的完全相同，包
      含每個收件者的錯誤。

exception smtplib.SMTPDataError

   SMTP 伺服器拒絕接受訊息資料。

exception smtplib.SMTPConnectError

   與伺服器建立連線的過程中發生錯誤。

exception smtplib.SMTPHeloError

   伺服器拒絕了我們的 "HELO" 訊息。

exception smtplib.SMTPNotSupportedError

   所嘗試的指令或選項不被伺服器支援。

   在 3.5 版被加入.

exception smtplib.SMTPAuthenticationError

   SMTP 身分驗證出錯。最有可能是伺服器不接受所提供的使用者名稱/密碼組
   合。

也參考:

  **RFC 821** - 簡易郵件傳輸協定
     SMTP 的協定定義。本文件涵蓋了 SMTP 的模型、操作流程和協定細節。

  **RFC 1869** - SMTP 服務擴充
     SMTP 的 ESMTP 擴充定義。這描述了一個以新指令擴充 SMTP 的框架，支
     援動態探查伺服器所提供的指令，並定義了一些額外的指令。


SMTP 物件
=========

"SMTP" 實例擁有以下方法：

SMTP.set_debuglevel(level)

   設定除錯輸出的層級。*level* 為 1 或 "True" 會為連線以及所有傳送至伺
   服器和從伺服器接收的訊息產生除錯訊息。*level* 為 2 則會為這些訊息加
   上時間戳記。

   在 3.5 版的變更: 新增 debuglevel 2。

SMTP.docmd(cmd, args='')

   傳送一個指令 *cmd* 給伺服器。選用引數 *args* 只會以一個空格分隔串接
   到指令後。

   這會回傳一個 2-tuple，由數值回應碼和實際的回應行所組成（多行回應會
   被合併為一長行）。

   在正常操作中，應該不需要明確地呼叫此方法。它被用於實作其他方法，並
   可能對測試私有擴充很有用。

   如果在等待回覆時與伺服器的連線中斷，則會引發
   "SMTPServerDisconnected"。

SMTP.connect(host='localhost', port=0)

   在給定的連接埠上連線到某個主機。預設值是連線到本地主機的標準 SMTP
   連接埠（25）。如果主機名稱以一個冒號（"':'"）後接一個數字結尾，該後
   綴會被去除，並將該數字解釋為要使用的連接埠號。如果在實例化期間有指
   定主機，建構函式會自動叫用此方法。回傳一個 2-tuple，內容為伺服器在
   其連線回應中所傳送的回應碼和訊息。

   If port is not changed from its default value of 0, the value of
   the "default_port" attribute is used.

   引發一個附帶引數 "self"、"host"、"port" 的稽核事件
   "smtplib.connect"。

SMTP.helo(name='')

   使用 "HELO" 向 SMTP 伺服器表明你的身分。hostname 引數預設為本地主機
   的完整網域名稱（fully qualified domain name）。伺服器回傳的訊息會被
   儲存為物件的 "helo_resp" 屬性。

   在正常操作中，應該不需要明確地呼叫此方法。必要時它會被 "sendmail()"
   隱式地呼叫。

SMTP.ehlo(name='')

   使用 "EHLO" 向 ESMTP 伺服器表明你的身分。hostname 引數預設為本地主
   機的完整網域名稱（fully qualified domain name）。檢視回應中的 ESMTP
   選項並將其儲存起來，供 "has_extn()" 使用。同時也會設定數個資訊性屬
   性：伺服器回傳的訊息會被儲存為 "ehlo_resp" 屬性，"does_esmtp" 會依
   伺服器是否支援 ESMTP 被設為 "True" 或 "False"，而 "esmtp_features"
   會是一個 dictionary，包含此伺服器所支援的 SMTP 服務擴充名稱以及它們
   的參數（如果有的話）。

   除非你想在寄送郵件前使用 "has_extn()"，否則應該不需要明確地呼叫此方
   法。必要時它會被 "sendmail()" 隱式地呼叫。

SMTP.ehlo_or_helo_if_needed()

   如果此 session 之前沒有過 "EHLO" 或 "HELO" 指令，此方法會呼叫
   "ehlo()" 和/或 "helo()"。它會先嘗試 ESMTP 的 "EHLO"。

   "SMTPHeloError"
      伺服器沒有正確地回覆 "HELO" 問候。

SMTP.has_extn(name)

   如果 *name* 在伺服器回傳的 SMTP 服務擴充集合中，則回傳 "True"，否則
   回傳 "False"。大小寫會被忽略。

SMTP.verify(address)

   使用 SMTP 的 "VRFY" 檢查某個位址在此伺服器上的有效性。如果使用者位
   址有效，則回傳一個由代號 250 和一個完整的 **RFC 822** 位址（包含人
   名）組成的 tuple。否則回傳一個 400 或更大的 SMTP 錯誤碼以及一個錯誤
   字串。

   備註:

     許多網站會停用 SMTP 的 "VRFY" 以阻擋垃圾郵件發送者。

SMTP.login(user, password, *, initial_response_ok=True)

   登入一個需要身分驗證的 SMTP 伺服器。引數為用來驗證的使用者名稱和密
   碼。如果此 session 之前沒有過 "EHLO" 或 "HELO" 指令，此方法會先嘗試
   ESMTP 的 "EHLO"。如果身分驗證成功，此方法會正常回傳，否則可能引發以
   下例外：

   "SMTPHeloError"
      伺服器沒有正確地回覆 "HELO" 問候。

   "SMTPAuthenticationError"
      伺服器不接受該使用者名稱/密碼組合。

   "SMTPNotSupportedError"
      伺服器不支援 "AUTH" 指令。

   "SMTPException"
      找不到合適的身分驗證方法。

   如果伺服器宣告支援 "smtplib" 所支援的每一種身分驗證方法，這些方法會
   被依序嘗試。支援的身分驗證方法清單請參閱 "auth()"。
   *initial_response_ok* 會被傳遞給 "auth()"。

   選用的關鍵字引數 *initial_response_ok* 指定對於支援它的身分驗證方法
   ，是否可以將 **RFC 4954** 中所指定的「initial response（初始回應）
   」隨 "AUTH" 指令一起傳送，而非要求進行 challenge（質詢）/response（
   回應）。

   在 3.5 版的變更: 可能會引發 "SMTPNotSupportedError"，並新增了
   *initial_response_ok* 參數。

SMTP.auth(mechanism, authobject, *, initial_response_ok=True)

   為指定的身分驗證 *mechanism* 發出一個 "SMTP" "AUTH" 指令，並透過
   *authobject* 處理 challenge 回應。

   *mechanism* 指定要用作 "AUTH" 指令引數的身分驗證機制；有效值為
   "esmtp_features" 的 "auth" 元素中所列出的那些。

   *authobject* 必須是一個接受單一選用引數的可呼叫物件：

      data = authobject(challenge=None)

   如果選用的關鍵字引數 *initial_response_ok* 為真，"authobject()" 會
   先在不帶引數的情況下被呼叫。它可以回傳 **RFC 4954** 的「initial
   response（初始回應）」ASCII "str"，該回應會如下被編碼並隨 "AUTH" 指
   令一起傳送。如果 "authobject()" 不支援初始回應（例如因為它需要一個
   challenge），則它在以 "challenge=None" 被呼叫時應回傳 "None"。如果
   *initial_response_ok* 為假，則 "authobject()" 不會先以 "None" 被呼
   叫。

   如果初始回應檢查回傳 "None"，或者如果 *initial_response_ok* 為假，
   "authobject()" 會被呼叫以處理伺服器的 challenge 回應；傳遞給它的
   *challenge* 引數會是一個 "bytes"。它應回傳會被 base64 編碼並傳送至
   伺服器的 ASCII "str" *data*。

   "SMTP" 類別為 "CRAM-MD5"、"PLAIN" 和 "LOGIN" 機制提供了
   "authobjects"；它們分別被命名為 "SMTP.auth_cram_md5"、
   "SMTP.auth_plain" 和 "SMTP.auth_login"。它們都要求 "SMTP" 實例的
   "user" 和 "password" 屬性被設定為適當的值。

   使用者程式碼通常不需要直接呼叫 "auth"，而是可以改為呼叫 "login()"
   方法，它會依所列順序依序嘗試上述每一種機制。"auth" 被公開出來是為了
   方便實作 "smtplib" 尚未（或還沒有）直接支援的身分驗證方法。

   在 3.5 版被加入.

SMTP.starttls(*, context=None)

   將 SMTP 連線置於 TLS（Transport Layer Security，傳輸層安全性）模式
   。後續所有的 SMTP 指令都會被加密。接著你應該再次呼叫 "ehlo()"。

   如果有提供 *keyfile* 和 *certfile*，它們會被用來建立一個
   "ssl.SSLContext"。

   選用的 *context* 參數是一個 "ssl.SSLContext" 物件；這是使用 keyfile
   和 certfile 的替代方案，如果有指定它，則 *keyfile* 和 *certfile* 都
   應為 "None"。

   如果此 session 之前沒有過 "EHLO" 或 "HELO" 指令，此方法會先嘗試
   ESMTP 的 "EHLO"。

   在 3.12 版的變更: 已棄用的 *keyfile* 和 *certfile* 參數已被移除。

   "SMTPHeloError"
      伺服器沒有正確地回覆 "HELO" 問候。

   "SMTPNotSupportedError"
      伺服器不支援 STARTTLS 擴充。

   "RuntimeError"
      你的 Python 直譯器無法使用 SSL/TLS 支援。

   在 3.3 版的變更: 新增 *context*。

   在 3.4 版的變更: 此方法現在支援以 "ssl.SSLContext.check_hostname"
   進行主機名稱檢查以及 *Server Name Indicator*（請參閱 "HAS_SNI"）。

   在 3.5 版的變更: 針對缺乏 STARTTLS 支援所引發的錯誤現在是
   "SMTPNotSupportedError" 子類別，而非基底的 "SMTPException"。

SMTP.sendmail(from_addr, to_addrs, msg, mail_options=(), rcpt_options=())

   寄送郵件。必要引數為一個 **RFC 822** 寄件位址字串、一個 **RFC 822**
   收件位址字串的 list（單一字串會被視為含有 1 個位址的 list），以及一
   個訊息字串。呼叫者可以傳入一個 ESMTP 選項的 list（例如 ""8bitmime""
   ）作為 *mail_options*，供 "MAIL FROM" 指令使用。應與所有 "RCPT" 指
   令一起使用的 ESMTP 選項（例如 "DSN" 指令）可以作為 *rcpt_options*
   傳入。每個選項都應作為一個包含該選項完整文字的字串傳入，包括任何可
   能的鍵（例如 ""NOTIFY=SUCCESS,FAILURE""）。（如果你需要對不同的收件
   者使用不同的 ESMTP 選項，你必須使用如 "mail()"、"rcpt()" 和
   "data()" 等低階方法來寄送訊息。）

   備註:

     *from_addr* 和 *to_addrs* 參數用來建構傳輸代理（transport agent）
     所使用的訊息信封（message envelope）。"sendmail" 不會以任何方式修
     改訊息標頭。

   *msg* 可以是一個包含 ASCII 範圍內字元的字串，或者一個位元組字串。字
   串會使用 ascii codec 被編碼為位元組，而單獨的 "\r" 和 "\n" 字元會被
   轉換為 "\r\n" 字元。位元組字串不會被修改。

   如果此 session 之前沒有過 "EHLO" 或 "HELO" 指令，此方法會先嘗試
   ESMTP 的 "EHLO"。如果伺服器支援 ESMTP，訊息大小和每個指定的選項都會
   被傳遞給它（如果該選項在伺服器宣告的功能集合中）。如果 "EHLO" 失敗
   ，則會嘗試 "HELO" 並抑制 ESMTP 選項。

   如果郵件至少被一個收件者接受，此方法會正常回傳。否則它會引發一個例
   外。也就是說，如果此方法沒有引發例外，那麼應該會有人收到你的郵件。
   如果此方法沒有引發例外，它會回傳一個 dictionary，其中每個被拒絕的收
   件者都有一個條目。每個條目都包含一個由 SMTP 錯誤碼和伺服器所傳送的
   隨附錯誤訊息組成的 tuple。

   如果 "SMTPUTF8" 被包含在 *mail_options* 中且伺服器支援它，則
   *from_addr* 和 *to_addrs* 可以包含非 ASCII 字元。

   此方法可能引發以下例外：

   "SMTPRecipientsRefused"
      所有收件者都被拒絕。沒有人收到郵件。

   "SMTPHeloError"
      伺服器沒有正確地回覆 "HELO" 問候。

   "SMTPSenderRefused"
      伺服器不接受 *from_addr*。

   "SMTPDataError"
      伺服器以一個非預期的錯誤碼回覆（而非拒絕某個收件者）。

   "SMTPNotSupportedError"
      *mail_options* 中有給定 "SMTPUTF8"，但伺服器不支援它。

   除非另有說明，否則即使在引發例外後，連線仍會保持開啟。

   在 3.2 版的變更: *msg* 可以是一個位元組字串。

   在 3.5 版的變更: 新增 "SMTPUTF8" 支援，且如果指定了 "SMTPUTF8" 但伺
   服器不支援它，可能會引發 "SMTPNotSupportedError"。

SMTP.send_message(msg, from_addr=None, to_addrs=None, mail_options=(), rcpt_options=())

   這是一個便利方法，用於以一個 "email.message.Message" 物件所表示的訊
   息呼叫 "sendmail()"。引數的意義與 "sendmail()" 的相同，但 *msg* 是
   一個 "Message" 物件。

   如果 *from_addr* 為 "None" 或 *to_addrs* 為 "None"，"send_message"
   會依 **RFC 5322** 中所指定的方式，以從 *msg* 的標頭中擷取出的位址填
   入這些引數：如果 *Sender* 欄位存在，*from_addr* 會被設為該欄位，否
   則設為 *From* 欄位。*to_addrs* 會結合 *msg* 中 *To*、*Cc* 和 *Bcc*
   欄位的值（如果有的話）。如果訊息中剛好出現一組 *Resent-** 標頭，則
   一般的標頭會被忽略，並改用 *Resent-** 標頭。如果訊息包含超過一組
   *Resent-** 標頭，則會引發 "ValueError"，因為無法明確地偵測出最近的
   那組 *Resent-* 標頭。

   "send_message" 會使用 "BytesGenerator" 並以 "\r\n" 作為 *linesep*
   來序列化 *msg*，並呼叫 "sendmail()" 來傳輸產生的訊息。無論
   *from_addr* 和 *to_addrs* 的值為何，"send_message" 都不會傳輸 *msg*
   中可能出現的任何 *Bcc* 或 *Resent-Bcc* 標頭。如果 *from_addr* 和
   *to_addrs* 中有任何位址包含非 ASCII 字元，且伺服器未宣告支援
   "SMTPUTF8"，則會引發 "SMTPNotSupportedError"。否則 "Message" 會以其
   "policy" 的副本（其中 "utf8" 屬性被設為 "True"）進行序列化，且
   "SMTPUTF8" 和 "BODY=8BITMIME" 會被新增到 *mail_options*。

   在 3.2 版被加入.

   在 3.5 版被加入: 對國際化位址（"SMTPUTF8"）的支援。

SMTP.quit()

   終止 SMTP session 並關閉連線。回傳 SMTP "QUIT" 指令的結果。

也支援對應於標準 SMTP/ESMTP 指令 "HELP"、"RSET"、"NOOP"、"MAIL"、
"RCPT" 和 "DATA" 的低階方法。通常這些方法不需要被直接呼叫，因此這裡不
對它們加以說明。詳情請參閱模組程式碼。

此外，SMTP 實例擁有以下屬性：

SMTP.helo_resp

   對 "HELO" 指令的回應，請參閱 "helo()"。

SMTP.ehlo_resp

   對 "EHLO" 指令的回應，請參閱 "ehlo()"。

SMTP.does_esmtp

   一個 boolean 值，指出伺服器是否支援 ESMTP，請參閱 "ehlo()"。

SMTP.esmtp_features

   一個包含伺服器所支援之 SMTP 服務擴充名稱的 dictionary，請參閱
   "ehlo()"。


SMTP 範例
=========

這個範例會提示使用者輸入訊息信封中所需的位址（「To」和「From」位址），
以及要傳遞的訊息。請注意，要隨訊息一起包含的標頭必須在輸入訊息時就包含
進去；這個範例不會對 **RFC 822** 標頭做任何處理。特別是，「To」和「
From」位址必須明確地包含在訊息標頭中：

   import smtplib

   def prompt(title):
       return input(title).strip()

   from_addr = prompt("From: ")
   to_addrs  = prompt("To: ").split()
   print("Enter message, end with ^D (Unix) or ^Z (Windows):")

   # 在開頭加上 From: 和 To: 標頭！
   lines = [f"From: {from_addr}", f"To: {', '.join(to_addrs)}", ""]
   while True:
       try:
           line = input()
       except EOFError:
           break
       else:
           lines.append(line)

   msg = "\r\n".join(lines)
   print("Message length is", len(msg))

   server = smtplib.SMTP("localhost")
   server.set_debuglevel(1)
   server.sendmail(from_addr, to_addrs, msg)
   server.quit()

備註:

  一般而言，你會想使用 "email" 套件的功能來建構電子郵件訊息，接著就可
  以透過 "send_message()" 來寄送它；請參閱 email：範例。
