python 发邮件 foxmail_python利用smtplib发邮件

本文介绍了如何使用Python的smtplib和email模块实现邮件的发送,包括正文、附件和图片。详细讲解了SMTP的工作流程,并提供了一个内部邮件服务器测试脚本,用于发送文本、图片和附件。在实际操作中可能会遇到认证问题,解决方法是调整smtplib支持的认证顺序或确保用户名和密码已进行Base64编码。
摘要由CSDN通过智能技术生成

python之所以强大,是因为它支持的库很多,光标准库就满足了大部分的常用需求,工作中遇到收发邮件过防火墙的场景,一端发一个病毒穿过防火墙到达另一端,检测是否能够被防火墙拦住,这个功能是非常普通的,但又非常重要,基本上每次必测之内容

SMTP一般收发流程

发信人在用户代理如foxmail中编辑邮件,包括填写发信人,收信人,标题等

用户代理提取发信人编辑的信息,生成一封符合邮件标准格式的(RFC822)的邮件

用户代理用SMTP将邮件发送到发送邮件服务器上(发信人邮箱所对应的sendmail server)

发送端邮件服务器用SMTP将邮件发送到接收邮件服务器上(pop3 server)

收信人再调用用户代理,用pop3协议取回邮件

用户代理解解析收到的邮件 ,以适当的形式呈现在收信人面前

测试流程:

在LAN端搭一个SMTP server

在WAN端用foxmail客户端连接SMTP server发送一个病毒给LAN端的PC

此时病毒以附件的形式发送到LAN Server, 穿过防火墙,防火墙可以检测的到

python实现自动发送邮件思路

smtplib模块负责发送邮件

email模块负责发送的内容

发送邮件分三种形式

正文

附件

图片

smtplib模块介绍

server = smtplib.SMTP(smtp server) 实例化SMTP

server.docmd(cmd) 仿telenet 邮箱服务器中直接输入命令

server.login(username,password) SMTP服务器需要验证用户才能发邮件

server.sendmail(from_mail, to_mail,msg) 核心转发程序,把msg从from_mail发送到to_mail邮箱中

email模块介绍

MIMEMultipart类负责多种类消息载体

MIMEImage类负责图片载体

MIMEText类负责文字载体

MIMEBase类负责一般文件附件载体

MIMEImage

__init__(self, _imagedata, _subtype=None, _encoder=, **_params)

MIMEImage(file(filename,'rb').read())

MIMEText

__init__(self, _text, _subtype='plain', _charset='us-ascii')

MIMEText(body_text,'html','utf-8')

MIMEBase

__init__(self, _maintype, _subtype, **_params)

MIMEBase('text',filename).set_payload(fp.read())

SMTP 发送邮件命令模式,理论上都可以用smtplib.SMTP(host).docmd()完成

[root@hding qa]# telnet 10.8.116.6 25

Trying 10.8.116.6...

Connected to 10.8.116.6 (10.8.116.6).

Escape character is '^]'.

220 hding.com ESMTP Sendmail 8.13.8/8.13.8; Tue, 16 Aug 2016 20:00:57 +0800

ehlo 10.8.116.6 #服务器支持信息

250-hding.com Hello [10.8.116.6], pleased to meet you

250-ENHANCEDSTATUSCODES

250-PIPELINING

250-8BITMIME

250-SIZE

250-DSN

250-ETRN

250-AUTH DIGEST-MD5 CRAM-MD5 LOGIN PLAIN #验证方式

250-STARTTLS #支持STARTTLS

250-DELIVERBY

250 HELP

auth login

334 VXNlcm5hbWU6

aGRpbmc= #只接受base64编码

334 UGFzc3dvcmQ6

aGRpbmc= #只接受base64编码

235 2.0.0 OK Authenticated

mail from: hding@hding.com

250 2.1.0 hding@hding.com... Sender ok

rcpt to : qa@ding.com

250 2.1.5 qa@ding.com... Recipient ok

data

354 Enter mail, end with "." on a line by itself

"hello world"

I am terry

please welcome me

. #结束正文

250 2.0.0 u7GC0v9x027721 Message accepted for delivery

quit

221 2.0.0 hding.com closing connection

Connection closed by foreign host.

内部邮件服务器测试脚本

#!/usr/bin/env python

#coding:utf-8

from smtplib import SMTP

from smtplib import SMTP_SSL

from email import encoders

from email.mime.text import MIMEText

from email.mime.multipart import MIMEMultipart

from email.mime.base import MIMEBase

from email.mime.image import MIMEImage

class DPI_SSL_SMTPS(object):

def __init__(self,from_mail, to_mail,subject,username='username',password='password', port=465,host='192.168.10.3'):

self.from_mail = from_mail

self.to_mail = to_mail

self.server = SMTP_SSL(host,port) #采取SMTP_SSL的方式,端口465

#self.server.starttls()

self.server.docmd('ehlo','192.168.10.3') #与服务器'ehlo'

self.server.login(username,password) #登录SMTP服务器

self.msg = MIMEMultipart() #定义消息载体

self.msg['From']= from_mail #消息from,to,subject字段不可省

self.msg['To']= to_mail

self.msg['Subject']=subject

def send_text(self,filename):

"send text as a body mail"

fp = open(filename,'r') #这边的正文内容是读取了一个文件中的内容写在正文中

content = fp.read()

fp.close()

con = MIMEText(content,'plain','utf-8')

self.msg.attach(con) #把正文内容加到消息载体中

def send_image(self,filename):

"send mail with image"

img = MIMEImage(file(filename,'rb').read()) #发送图片附件

img.add_header('Content-ID','1') #增加头信息cid,是为了以后如果要在正文中引用""'

img.add_header("Content-Disposition","attachment",filename=filename) #增加附件

self.msg.attach(img) #图片附件进入消息载体

def send_attachment(self,filename):

"send mail with attachment"

fp = open(filename,'rb')

mime = MIMEBase('text',filename)

mime.add_header('Content-Disposition','attachment',fiename=filename)

mime.set_payload(fp.read()) #把文件中的内容写进payload作为附件

encoders.encode_base64(mime)

fp.close()

self.msg.attach(mime)

def send_mail(self):

self.server.sendmail(self.from_mail,self.to_mail,self.msg.as_string())

self.server.quit()

if __name__ == '__main__':

server = DPI_SSL_SMTPS('hding@hding.com','qa@ding.com','test_mail')

server.send_attachment("test_file")

server.send_image('2.jpg')

server.send_text("test_file")

server.send_mail()

遇到的问题

smtplib.SMTPAuthenticationError: (535, 'Error: authentication failed')

这个问题是认证的问题,但是用命令行已经验证了用户口令正确,但执行时就会提示错误,原因在于认证的方式不同,命令行用的是AUTH_LOGIN,而smtplib时的login函数用的却是MD5,查看smtplib.py文件

def login(self, user, password):

"""Log in on an SMTP server that requires authentication.

The arguments are:

- user: The user name to authenticate with.

- password: The password for the authentication.

If there has been no previous EHLO or HELO command this session, this

method tries ESMTP EHLO first.

This method will return normally if the authentication was successful.

This method may raise the following exceptions:

SMTPHeloError The server didn't reply properly to

the helo greeting.

SMTPAuthenticationError The server didn't accept the username/

password combination.

SMTPException No suitable authentication method was

found.

"""

def encode_cram_md5(challenge, user, password):

challenge = base64.decodestring(challenge)

response = user + " " + hmac.HMAC(password, challenge).hexdigest()

return encode_base64(response, eol="")

def encode_plain(user, password):

return encode_base64("\0%s\0%s" % (user, password), eol="")

AUTH_PLAIN = "PLAIN"

AUTH_CRAM_MD5 = "CRAM-MD5"

AUTH_LOGIN = "LOGIN"

self.ehlo_or_helo_if_needed()

if not self.has_extn("auth"):

raise SMTPException("SMTP AUTH extension not supported by server.")

# Authentication methods the server supports:

authlist = self.esmtp_features["auth"].split()

# List of authentication methods we support: from preferred to

# less preferred methods. Except for the purpose of testing the weaker

# ones, we prefer stronger methods like CRAM-MD5:

preferred_auths = [AUTH_CRAM_MD5, AUTH_PLAIN, AUTH_LOGIN]

# Determine the authentication method we'll use

authmethod = None

for method in preferred_auths:

if method in authlist:

authmethod = method

break

if authmethod == AUTH_CRAM_MD5:

(code, resp) = self.docmd("AUTH", AUTH_CRAM_MD5)

if code == 503:

# 503 == 'Error: already authenticated'

return (code, resp)

(code, resp) = self.docmd(encode_cram_md5(resp, user, password))

elif authmethod == AUTH_PLAIN:

(code, resp) = self.docmd("AUTH",

AUTH_PLAIN + " " + encode_plain(user, password))

elif authmethod == AUTH_LOGIN:

(code, resp) = self.docmd("AUTH",

"%s %s" % (AUTH_LOGIN, encode_base64(user, eol="")))

if code != 334:

raise SMTPAuthenticationError(code, resp)

(code, resp) = self.docmd(encode_base64(password, eol=""))

elif authmethod is None:

raise SMTPException("No suitable authentication method found.")

if code not in (235, 503):

# 235 == 'Authentication successful'

# 503 == 'Error: already authenticated'

raise SMTPAuthenticationError(code, resp)

return (code, resp)

preferred_auths = [AUTH_CRAM_MD5, AUTH_PLAIN, AUTH_LOGIN]列表第一个是MD5,因此匹配到MD5就直接MD5认证了,如果想LOGIN认证的话,只需要把列表的顺序改一下就好

preferred_auths = [AUTH_LOGIN,AUTH_CRAM_MD5, AUTH_PLAIN] 即可

当然还容易遇到这个问题的原因是因为smtp的用户名和密码必需是经过base64编码过的,如果用c或其它语方纯编的话,这边就要小心,需要编码,而smtplib没有这个顾虑,因为已经写好了

elif authmethod == AUTH_LOGIN:

(code, resp) = self.docmd("AUTH",

"%s %s" % (AUTH_LOGIN, encode_base64(user, eol="")))

if code != 334:

raise SMTPAuthenticationError(code, resp)

(code, resp) = self.docmd(encode_base64(password, eol=""))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值