python有很多强大的module,邮箱相关的就有smtplib,sendmail等,但是今天ichram想使用socket来实现发送邮件的功能,也是为了学习python,练手为主。
SMTP
smtp是simple mail tranfer protocal的简写,是定义在应用层上的协议,通信的过程双方使用SMTP命令码交换信息,详细资料可以参考 http://www.cnpaf.net/rfc/rfc821.txt 。
源码
下面代码托管于github: https://github.com/icharm/pyTools/blob/master/sendmail.py
#this is a simple demo to send mail by socket module import socket import time import base64 import md5 import random PATH = "log/sendmail.log" #getTime according special format def getTime(format): #return the time format like 2017-07-17 15:34:23 if(format == 1): ISOTIMEFORMAT = '%Y-%m-%d %X' return time.strftime(ISOTIMEFORMAT, time.localtime()); #return the time format like Sun Jun 15:34:23 2017 elif(format == 2): return time.asctime( time.localtime(time.time()) ) def printErr(msg): print "[-]Error "+ getTime(1) + ' ' + msg +'\n' def saveLog(msg, flag): if flag == 1: log = '[-]Error '+ getTime(1) + ' ' + msg +'\n' elif flag == 2: log = '[-]Warning '+ getTime(1) + ' ' + msg + '\n' elif flag == 3: log = '[*]Debug '+ getTime(1) + ' '+ msg + '\n' else: log = '[+]Normal '+ getTime(1) + ' '+ msg + '\n' try: f = open(PATH, 'a') f.write(log) f.close() except Exception, e: printErr(str(e)) #使用smtp命令码与服务器通信 #fr: mail form #to: mail to #data: mail content def sendMailCore(fr, to, data): s = socket.socket() #make sure to connect server try: while True: s.connect(("10.204.148.47", 25)) banner = s.recv(1024) if banner[:3] == '220': break else: saveLog("Connect return: "+ banner, 3) while True: s.send("EHLO kali.mei.com\r\n") ret = s.recv(1024) if ret[:3] == '250': break else: saveLog("EHLO return: "+ banner, 3 ) while True: s.send("MAIL FROM:<"+ fr +">\r\n") ret = s.recv(1024) if ret[:3] == '250': break else: saveLog("FROM return: "+ banner, 3) while True: s.send("RCPT TO:<"+ to +">\r\n") ret = s.recv(1024) if ret[:3] == '250': break else: saveLog("RCPT return: "+ banner, 3) while True: s.send("DATA\r\n") ret = s.recv(1024) if ret[:3] == '354': break else: saveLog("DATA return:"+ banner, 3) while True: s.send(data.encode('utf-8')) s.send("\r\n.\r\n") ret = s.recv(1024) if ret[:3] == '250': break else: saveLog("DATA OVER return: "+ banner, 3) while True: s.send("QUIT\r\n") ret = s.recv(1024) if ret[:3] == '221': break else: saveLog("Line 79 Error", 3) s.close() return True except Exception, e: saveLog(str(e), 1) return False def makeContent(fr, to, subject, data, isHtml=False, Path=None): #构造邮件头 和 MIMEtext 分段 #需要注意的就是boundary="-----=_Part_{random}_=-----" 这行定义了一个邮件的分段标志,这里假设分段标志为A,即boundary="A"。则每一个内容分段开头一行必须是--A,而邮件的结尾必须有A-- mail_template = '''Date: {date}\r\nFrom: <{from}>\r\nTo: <{to}>\r\nSubject: {subject}\r\nX-priority: {priority}\r\nX-Mailer: Charmail 1.0.0[cn]\r\nMIME-Version: 1.0\r\nContent-Type: multipart/alternative;\r\n\tboundary="-----=_Part_{random}_=-----"\r\n{content}\r\n-------=_Part_{random}_=-------''' #构造邮件的一个内容分段(该分段的内容类型为text/plain) #需要注意的是内容的分段必须以上面定义的分段标志开始,且在分段标志的最前面加上两个短横杠-- text_content_template = '''-------=_Part_{random}_=-----\r\nContent-Type: text/plain;\n\tcharset="UTF-8"\r\nContent-Transfer-Encoding: base64\r\n\r\n{content}\r\n''' #构造邮件的一个内容分段(该分段的内容类型为text/html) html_content_template = '''-------=_Part_{random}_=-----\r\nContent-Type: text/html;\r\n\tcharset=UTF-8\r\nContent-Transfer-Encoding: quoted-printable\r\n{content}\r\n''' #构造邮件的一个内容分段(该分段的内容类型为zip附件) attachment_content_template='''-------=_Part_{random}_=-----\r\nContent-Type: {type};\r\n\tname={name}\r\nContent-Transfer-Encoding: base6\r\nContent-Disposition: attachment;\r\n\tfilename={name}\r\n{content}\r\n''' #MIME 支持的文件类型有很多,详细的可以参考http://www.w3school.com.cn/media/media_mimeref.asp attachment_type = ['text/plain', 'application/zip', 'image/jpeg'] #生成一个随机码来表示邮件分段 #这里icharm是使用随机的截取forn,to,data中的任意长度的字符重新结合并进行md5加密,得到的随机码,理论上重复的可能性很小,但是写的有点麻烦,不推荐 fr_len = len(fr) fr_len_2 = int(fr_len/2) to_len = len(to) to_len_2 = int(to_len/2) sub_len = len(subject) sub_len_2 = int(sub_len/2) rand = fr[random.randint(0,fr_len_2) : random.randint(fr_len_2,fr_len)] + to[ random.randint(0,to_len_2) : random.randint(to_len_2, to_len)] + subject[ random.randint(0,sub_len_2) : random.randint(sub_len_2, sub_len)] md = md5.new() md.update(rand) rand = md.hexdigest() if isHtml == True: content = html_content_template content = content.replace('{content}', data) content = content.replace('{random}', rand) else: content = text_content_template content = content.replace('{content}', base64.b64encode(data)) content = content.replace('{random}', rand) #make attachment content later #make the total mail content mail_template = mail_template.replace('{date}', getTime(2)) mail_template = mail_template.replace('{from}', fr) mail_template = mail_template.replace('{to}', to) mail_template = mail_template.replace('{subject}', subject) mail_template = mail_template.replace('{priority}', '3') mail_template = mail_template.replace('{random}', rand, 5) mail_template = mail_template.replace('{content}', content) return mail_template def sendMail(fr, to, subject, data, isHtml=False, Path=None ): return sendMailCore(fr, to, makeContent(fr, to, subject, data, isHtml, Path) ) def main(): result = sendMail('a@icharm.me', 'b@icharm.me', 'Charmail test' ,''' Mail come form Charmail By icharm.me ''') if result == True: print "Successed" saveLog("Mail Send Success", 5) else: print "Failed" saveLog("Send Mail Failed", 3) if __name__ == '__main__': main()
参考
http://www.cnpaf.net/rfc/rfc821.txt
http://www.w3school.com.cn/media/media_mimeref.asp
Comments | NOTHING