python模組之smtplib: 用python發送SSL/TLS安全郵件

來源:互聯網
上載者:User

python的smtplib提供了一種很方便的途徑寄送電子郵件。它對smtp協議進行了簡單的封裝。
     smtp協議的基本命令包括:
         HELO 向伺服器標識使用者身份
         MAIL 初始化郵件傳輸 mail from:
         RCPT 標識單個的郵件接收人;常在MAIL命令後面,可有多個rcpt to:
         DATA 在單個或多個RCPT命令後,表示所有的郵件接收人已標識,並初始化資料轉送,以.結束
         VRFY 用於驗證指定的使用者/郵箱是否存在;由於安全方面的原因,伺服器常禁止此命令
         EXPN 驗證給定的郵箱列表是否存在,擴充郵箱列表,也常被禁用
         HELP 查詢服務器支援什麼命令
         NOOP 無操作,伺服器應響應OK
         QUIT 結束會話
         RSET 重設會話,當前傳輸被取消
         MAIL FROM 指定寄件者地址
         RCPT TO 指明的接收者地址
     一般smtp會話有兩種方式,一
種是郵件直接投遞,就是說,比如你要發郵件給zzz@163.com,那就直接連接163.com的郵件伺服器,把信投給zzz@163.com;
另一種是驗證過後的發信,它的過程是,比如你要發郵件給zzz@163.com,你不是直接投到163.com,而是通過自己在sina.com的另一個
郵箱來發。這樣就要先串連sina.com的smtp伺服器,然後認證,之後在把要發到163.com的信件投到sina.com上,sina.com會
幫你把信投遞到163.com。

     第一種方式的命令流程基本是這樣:
         1. helo
         2. mail from
         3. rcpt to
         4. data
         5. quit
     但是第一種發送方式一般有限制的,就是rcpt to指定的這個郵件接收者必須在這個伺服器上存在,否則是不會接收的。先看看代碼:
#-*- encoding: gb2312 -*-
import os, sys, string
import smtplib

# 郵件伺服器地址
mailserver = "smtp.163.com"
# smtp會話過程中的mail from地址
from_addr = "asfgysg@zxsdf.com"
# smtp會話過程中的rcpt to地址
to_addr = "zhaoweikid@163.com"
# 信件內容
msg = "test mail"

svr = smtplib.SMTP(mailserver)
# 設定為偵錯模式,就是在會話過程中會有輸出資訊
svr.set_debuglevel(1)
# helo命令,docmd方法包括了擷取對方伺服器返回資訊
svr.docmd("HELO server")
# mail from, 發送郵件寄件者
svr.docmd("MAIL FROM: <%s>" % from_addr)
# rcpt to, 郵件接收者
svr.docmd("RCPT TO: <%s>" % to_addr)
# data命令,開始發送資料
svr.docmd("DATA")
# 發送本文資料
svr.send(msg)
# 比如以/r/n./r/n作為本文發送結束的標記,用send發送的,所以要用getreply擷取返回資訊
svr.send("/r/n./r/n")
svr.getreply()
# 發送結束,退出
svr.quit()

     第二種有點不一樣:
         1.ehlo
         2.auth login
         3.mail from
         4.rcpt to
         5.data
         6.quit
相對於第一種來說,多了一個認證過程,就是auth login這個過程。
#-*- encoding: gb2312 -*-
import os, sys, string
import smtplib
import base64

# 郵件伺服器地址
mailserver = "smtp.163.com"
# 郵件使用者名
username = "xxxxxx@163.com"
# 密碼
password = "xxxxxxx"
# smtp會話過程中的mail from地址
from_addr = "xxxxxx@163.com"
# smtp會話過程中的rcpt to地址
to_addr = "yyyyyy@163.com"
# 信件內容
msg = "my test mail"

svr = smtplib.SMTP(mailserver)
# 設定為偵錯模式,就是在會話過程中會有輸出資訊
svr.set_debuglevel(1)
# ehlo命令,docmd方法包括了擷取對方伺服器返回資訊
svr.docmd("EHLO server")
# auth login 命令
svr.docmd("AUTH LOGIN")
# 發送使用者名稱,是base64編碼過的,用send發送的,所以要用getreply擷取返回資訊
svr.send(base64.encodestring(username))
svr.getreply()
# 發送密碼
svr.send(base64.encodestring(password))
svr.getreply()
# mail from, 發送郵件寄件者
svr.docmd("MAIL FROM: <%s>" % from_addr)
# rcpt to, 郵件接收者
svr.docmd("RCPT TO: <%s>" % to_addr)
# data命令,開始發送資料
svr.docmd("DATA")
# 發送本文資料
svr.send(msg)
# 比如以/r/n./r/n作為本文發送結束的標記
svr.send("/r/n./r/n")
svr.getreply()
# 發送結束,退出
svr.quit()

     上面說的是最普通的情況,但是不能忽略的是現在好多企業郵件是支援安全郵件的,就是通過SSL發送的郵件,這個怎麼發呢?SMTP對SSL安全郵件的支援有兩種方案,一種老的是專門開啟一個465連接埠來接收ssl郵件,另一種更新的做法是在標準的25連接埠的smtp上增加一個starttls的命令來支援。

     看看第一種怎麼辦:
#-*- encoding: gb2312 -*-
import os, sys, string, socket
import smtplib

class SMTP_SSL (smtplib.SMTP):
     def __init__(self, host='', port=465, local_hostname=None, key=None, cert=None):
         self.cert = cert
         self.key = key
         smtplib.SMTP.__init__(self, host, port, local_hostname)
        
     def connect(self, host='localhost', port=465):
         if not port and (host.find(':') == host.rfind(':')):
             i = host.rfind(':')
             if i >= 0:
                 host, port = host[:i], host[i+1:]
                 try: port = int(port)
                 except ValueError:
                     raise socket.error, "nonnumeric port"
         if not port: port = 654
         if self.debuglevel > 0: print>>stderr, 'connect:', (host, port)
         msg = "getaddrinfo returns an empty list"
         self.sock = None
         for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
             af, socktype, proto, canonname, sa = res
             try:
                 self.sock = socket.socket(af, socktype, proto)
                 if self.debuglevel > 0: print>>stderr, 'connect:', (host, port)
                 self.sock.connect(sa)
                 # 新增加的建立ssl串連
                 sslobj = socket.ssl(self.sock, self.key, self.cert)
             except socket.error, msg:
                 if self.debuglevel > 0:
                     print>>stderr, 'connect fail:', (host, port)
                 if self.sock:
                     self.sock.close()
                 self.sock = None
                 continue
             break
         if not self.sock:
             raise socket.error, msg

         # 設定ssl
         self.sock = smtplib.SSLFakeSocket(self.sock, sslobj)
         self.file = smtplib.SSLFakeFile(sslobj);

         (code, msg) = self.getreply()
         if self.debuglevel > 0: print>>stderr, "connect:", msg
         return (code, msg)
        
if __name__ == '__main__':
     smtp = SMTP_SSL('192.168.2.10')
     smtp.set_debuglevel(1)
     smtp.sendmail("zzz@xxx.com", "zhaowei@zhaowei.com", "xxxxxxxxxxxxxxxxx")
     smtp.quit()

     這裡我是從原來的smtplib.SMTP派生出了新的SMTP_SSL類,它專門來處理ssl串連。我這裡測試的192.168.2.10是我自己的測試伺服器.
     第二種是新增加了starttls的命令,這個很簡單,smtplib裡就有這個方法,叫smtplib.starttls()。那麼相對於發送普通郵件的第二種方法來說,只需要新增加一行代碼就可以了:

#-*- encoding: gb2312 -*-
import os, sys, string
import smtplib
import base64

# 郵件伺服器地址
mailserver = "smtp.163.com"
# 郵件使用者名
username = "xxxxxx@163.com"
# 密碼
password = "xxxxxxx"
# smtp會話過程中的mail from地址
from_addr = "xxxxxx@163.com"
# smtp會話過程中的rcpt to地址
to_addr = "yyyyyy@163.com"
# 信件內容
msg = "my test mail"

svr = smtplib.SMTP(mailserver)
# 設定為偵錯模式,就是在會話過程中會有輸出資訊
svr.set_debuglevel(1)
# ehlo命令,docmd方法包括了擷取對方伺服器返回資訊
svr.docmd("EHLO server")
svr.starttls() # <------ 這行就是新加的支援安全郵件的代碼!

# auth login 命令
svr.docmd("AUTH LOGIN")
# 發送使用者名稱,是base64編碼過的,用send發送的,所以要用getreply擷取返回資訊
svr.send(base64.encodestring(username))
svr.getreply()
# 發送密碼
svr.send(base64.encodestring(password))
svr.getreply()
# mail from, 發送郵件寄件者
svr.docmd("MAIL FROM: <%s>" % from_addr)
# rcpt to, 郵件接收者
svr.docmd("RCPT TO: <%s>" % to_addr)
# data命令,開始發送資料
svr.docmd("DATA")
# 發送本文資料
svr.send(msg)
# 比如以/r/n./r/n作為本文發送結束的標記
svr.send("/r/n./r/n")
svr.getreply()
# 發送結束,退出
svr.quit()

注意: 以上的代碼為了方便我都沒有判斷傳回值,嚴格說來,是應該判斷一下返回的代碼的,在smtp協議中,只有傳回碼是2xx或者3xx才能繼續下一步,返回4xx或5xx的,都是出錯了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.