最後更新: 2016-07-25
smtplib 與 mail
目錄
- smtplib - TLS
- smtplib -SSL
- smtplib -Debug TLS 及 SSL
- smtplib - Exception
- Date
- mail - class
- mail - Example 1~3
- Embed image
TLS
TLS default port 587
#!/usr/bin/python
import smtplib
myserver = ""
myfrom = ""
myto = ""
loginuser = ""
loginpw = ""
print "Testing Server: " + myserver
msg = ("From: %s \r\nTo: %s \r\nSubject: test mail %s \r\n\r\nServer: %s"
% (myfrom, myto, myserver, myserver))
# class smtplib.SMTP([host[, port[, local_hostname[, timeout]]]])
server = smtplib.SMTP(myserver, timeout=3)
# 非必要
server.set_debuglevel(1)
# 當有 login 時, 一定要用 ehlo()
server.ehlo()
# 非必要
server.starttls()
# You should then call ehlo() again.(非必要)
server.ehlo()
# 登入才 send 信
server.login(loginuser, loginpw)
# server.sendmail(fromaddr, toaddrs, msg)
server.sendmail(myfrom, myto, msg)
server.quit()
Remark
設定 hello name
SMTP.helo([hostname]) / SMTP.ehlo([hostname])
SSL
用 SSL 時要把
smtplib.SMTP('your_smtp_gw')
改成
server = smtplib.SMTP_SSL(myserver, 465, timeout=3) # If the timeout expires, "socket.timeout" is raised.
P.S.
# port: 465
Debug TLS 及 SSL
SSL:
openssl s_client -connect mail.server:465
TLS:
openssl s_client -connect server:587 -starttls smtp [–crlf]
-starttls protocol # send the protocol-specific message(s) to switch to TLS for communication.
–crlf # line-terminator “<LF>” or “<CRLF>”
----
Troubleshoot 1
ssl.SSLError: [SSL: DH_KEY_TOO_SMALL] dh key too small (_ssl.c:727)
Server Log
...: warning: TLS library problem: 6477:error:14094410:SSL routines:SSL3_READ_BYTES: sslv3 alert handshake failure:s3_pkt.c:1257:SSL alert number 40:
只適用於 Python 3: 加 ssl 的 context
import ssl ... #context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_1) context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) connection.starttls(context=context)
smtplib 的 Exception
smtplib.SMTPException # base
smtplib.SMTPConnectError
smtplib.SMTPServerDisconnected # unexpectedly disconnects
smtplib.SMTPHeloError
smtplib.SMTPRecipientsRefused
smtplib.SMTPSenderRefused
smtplib.SMTPDataError
smtplib.SMTPAuthenticationError
smtplib.SMTPResponseException # SMTP error code
i.e.
try: server.sendmail(myfrom, myto, msg) except smtplib.SMTPException as e: print e else: print "test OK"
output:
{'x@x': (554, '5.7.1 <x@x>: Recipient address rejected: Over send mail limit')}
E-Mail 內的 Date
# Format: RFC 2822 (Fri, 09 Nov 2001 01:08:47 -0000)
email.utils.formatdate([timeval[, localtime][, usegmt]])
Optional
timeval if given is a floating point time value as accepted by time.gmtime() and time.localtime() (Default: current time)
localtime is a flag that when True, interprets timeval, and returns a date relative to the local timezone instead of UTC (Default: False)
usegmt, outputs a date string with the timezone as an ascii string GMT, rather than a numeric -0000 (Default: False)
code
from email.utils import formatdate
formatdate(localtime=True)
out
'Wed, 12 Oct 2016 18:06:10 +0800'
mail class
MIMEBase - MIMENonMultipart (直接是信的內容)
- MIMEMultipart - MIMEImage, MIMEText
class email.mime.base.MIMEBase
# base class for all the MIME-specific subclasses of Message
# Ordinarily you won’t create instances specifically of MIMEBaseclass
email.mime.nonmultipart.MIMENonMultipart()
# A subclass of MIMEBase
# prevent the use of the attach() method (MultipartConversionError exception is raised)
class email.mime.multipart.MIMEMultipart([_subtype[, boundary[, _subparts[, _params]]]])
# A subclass of MIMEBase
# this is an intermediate base class for MIME messages that are multipart.
- A Content-Type header of multipart/_subtype will be added to the message object
- A MIME-Version header will also be added.
defaults:
_subtype: mixed
email.mime.image.MIMEImage(_imagedata)
# A subclass of MIMENonMultipart
class email.mime.text.MIMEText(_text [, _subtype[, _charset]])
# A subclass of MIMENonMultipart
# _text is the string for the payload
# _subtype: defaults to plain
# _charset: default us-ascii
i.e. utf-8
msg = MIMEMultipart() msg.attach(MIMEText(texto.encode('utf-8'), 'plain', 'utf-8'))
class email.message.Message
# Representing an email message
__setitem__(name, val)
# Add a header to the message with field name name and value val (append)
* not overwrite or delete any existing header with the same name
__delitem__(name)
# Delete "all" occurrences of the field with name from the message’s headers
* not present => No exception is raised
__getitem__(name)
# Return the value of the named header field.
* missing => None * more than once => undefined
__len__()
# total number of headers
* including duplicates
__contains__(name)
* Matching is done case-insensitively
__str__()
# Equivalent to as_string(unixfrom=True)
attach(payload)
# Add the given payload to the current payload,
as_string([unixfrom=False, maxheaderlen=0, policy=None])
# Return the entire message flattened as a string.
# When unixfrom is True, the envelope header is included in the returned string.
# envelope 就是這一行: "From nobody Mon Aug 18 16:21:59 2014"
Example
Example 1A: 設定 "From", "To" 及內容及多個收件者
import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText COMMASPACE = ', ' MyFrom = sender@domain MyTo = ("A@domain", "B@domain") # Create the container (outer) email message. msg = MIMEMultipart() # 設定 from 及 to msg['Subject'] = 'Our family reunion' msg['From'] = me msg['To'] = COMMASPACE.join(MyTo) # 設定內容 msg.attach(MIMEText("testing")) # 特別內容, e-mail client 會 ignore 佢的 msg.preamble = '-===================-' # Send the email via our own SMTP server. s = smtplib.SMTP('localhost') s.sendmail(MyFrom, MyTo, msg.as_string()) s.quit()
msg.preamble=""
allows for some text between the blank line following the headers, and the first multipart boundary string
Normally, this text is never visible in a MIME-aware mail reader, because it falls outside the standard MIME armor.
i.e
# Mail Header 有
Content-Type: multipart/mixed; boundary="===============1331787088682035175=="
# Mail body 有以下內容
# msg.attach(MIMEText("testing"))
# msg.preamble = '-===================-'
-===================-
--===============1331787088682035175==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
testing
--===============1331787088682035175==--
Example2: Mail Header
Spam check
MISSING_DATE=1.36, MISSING_MID=0.497
code
from email.mime.multipart import MIMEMultipart from email.utils import formatdate .. msg = MIMEMultipart() msg['Message-ID']="<UUID_x@y>" msg['Date']=formatdate(localtime=True) ...
Example 3: 只有 MIMEMultipart 才可以 call attach !!
import smtplib from email.MIMEMultipart import MIMEMultipart from email.MIMEText import MIMEText from email.MIMEImage import MIMEImage .... msg = MIMEMultipart() msg.attach(MIMEText(file("text.txt").read())) msg.attach(MIMEImage(file("image.png").read())) # to send mailer = smtplib.SMTP() mailer.connect() mailer.sendmail(from, to, msg.as_string()) mailer.close()
Example 4: Attachment Filename
filename = "lime.jpg" myimg = file(filename, "rb").read() myimg = MIMEImage(myimg) myimg.add_header('Content-Disposition', 'attachment; filename="%s"'% filename) msg.attach(myimg)
Example 5: Other type attachment
from email import Encoders from email.MIMEBase import MIMEBase filename="test.txt" part = MIMEBase('application', "octet-stream") part.set_payload( file(filename,"rb").read() ) Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="%s"'% filename) msg.attach(part)
Encoders
Encodes the payload into base64 form and sets the Content-Transfer-Encoding header to base64
encode 前
Content-Type: application/octet-stream MIME-Version: 1.0 測試
encode 後
Content-Type: application/octet-stream
MIME-Version: 1.0
Content-Transfer-Encoding: base64
tPq41Q==
相關的 mail function
- set_payload(payload[, charset])
- get_payload([i[, decode]])
P.S.
zip attachment
from email.mime.base import MIMEBase from email import encoders .................... filename = "test.zip" part = MIMEBase('application', "octet-stream") part.set_payload(file(filename, "rb").read()) encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="%s"'% filename) msg.attach(part) ....................
raw
--===============8891299427962483695== Content-Type: application/octet-stream MIME-Version: 1.0 Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="test.bin" AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ...........................
Embed image
Content-Type:
Content-Type: multipart/related;
boundary="===============ID2=="
HTML:
--===============ID2== <!-- cid (Content-ID) --> <img src="cid:123456789" alt=""> ......................
Resource:
--===============ID2== Content-Type: image/png; Content-ID: <123456789> ....................... --===============ID2==--