天天看點

使用python接收imap郵件

最近研究邮件透明加密技术,需要测试imap服务,然而邮件客户端不好用所以需要自己编写代码,这里使用python编写。这里关于邮件透明加密技术,推荐天御云安的隐密邮,部署简单,不影响用户使用习惯,非常好的一款产品。网址:https://mail.tyyunan.com/

import getpass, imaplib
import time
import pickle
import email,string,sys,os
from email.header import Header
from email.header import decode_header
from time import strptime, strftime
from email.parser import Parser
import hashlib

ISOTIMEFORMAT="%Y-%m-%d %X"
#定义邮件服务器常量
IMAPSever='192.168.61.4'
IMAPPort='143'
IMAPUser='[email protected]'
IMAPPassword='123456'
tmp_dir='E:\\python\\'
def GetFileMd5(filename):
    
    try:
        if not os.path.isfile(filename):
            return None
        myhash = hashlib.md5()
        f = open(filename, 'rb')
        while True:
            b = f.read(8096)
            if not b: break
            myhash.update(b)
        f.close()
        return myhash.hexdigest()
    except :
        return None
#def writelog(log):
#    fn = tmp_dir + 'log.txt'
#    f = open(fn, 'a+')
#    f.write(log+'\n')
#    f.close
def writelog(log):
    f = open('E:\\log.txt', 'a+')
    f.write(log+'\n')
    f.close
def test_log(log):
    fn = tmp_dir + 'test_log.txt'
    f = open(fn, 'a+')
    f.write(log)
    f.close    
##附件MD5映射表
#attr_md5_dic = {'c:\\tmp1\imap\\1_python.eml':'db38f30bf46982b51998acf7878586ed',
#                'c:\\tmp1\imap\\2_python.eml':'f6865b3e0e0d5592dc94c0db2c397c0c',
#                'c:\\tmp1\\imap\\3_python.eml':'a51e1fca402febe65e7a097cea299a43',
#                'c:\\tmp1\\imap\\4_python.eml':'2ecd56ec0dac8fd0742763be9c756a5f',
#                'c:\\tmp1\\imap\\5_python.eml':'9218a63c8bce5b1ff57d02deb8ba03b5',
#                'c:\\tmp1\\imap\\6_python.eml':'36e1ae8414a39f3af757a1b47d669191',
#                'c:\\tmp1\\imap\\7_python.eml':'2f34d001116515b35e2edbb79e4c229c',
#                'c:\\tmp1\\imap\\8_python.eml':'8c12c98b9062b2a1c16bedb21d554135',
#                'c:\\tmp1\\imap\\9_python.eml':'0b8ffc79c11a0dfb2f75291dfd75f29b',
#                'c:\\tmp1\\imap\\10_python.eml':'b5451f68d6cb9cec7403affd2b5157ed',
#                'c:\\tmp1\\imap\\11_python.eml':'895c22a04d6d574bbf6cdd050b9e0162',
#                'c:\\tmp1\\imap\\12_python.eml':'42bacecc8c6746a3878bfb0157e48440',
#                'c:\\tmp1\\imap\\13_python.eml':'2f7847fb47c3f94add85dcf71eebb89b',
#                'c:\\tmp1\\imap\\14_python.eml':'db3b78eae3146fb5b7c858849fb93084'
#                }
#附件MD5映射表
attr_md5_dic = {'1.jpg':'7C6426E4F8CD8B12D6CDE3DFDB8F2E9F',
                '1M-good.txt':'D1240F099E53AF249541AC95B7B0FA95',
                '2.jpg':'B167F5EE9B7F12E615106D0DE5A194C6',
                '3.jpg':'622E1A2410C21667F731FD2D5C67596F',
                '4.pdf':'DCB7434FCA1034EBA641EBFEE23C7283',
                '5.pdf':'5DBC1699C1C0CABE08E571654885693E',
                '6.rar':'0031E302AEABD0262B0F38FA537DF0BE',
                '7.docx':'3F471FC456F1917AE282022ECD6DDB5D',
                '8.docx':'4684DFA7FE8B87B0181855AA095D9C9D',
                '9.7z':'08C9CE615196E95475DA832E2A7C43AF',
                '10.xlsx':'24895653E8D04D2F3E756964A44A274E',
                '11.txt':'4973E43DB511BA94A4A4F96DC0AD161A',
                '12.txt':'2F0670DA4398CFC7E04DC6D7F77F8411',
                '13.txt':'D51BEB408B4829BA895B2A5F7AF10894',
                '14.txt':'066F8F57FBC8F77698CDA4B72B45C129',
                '15.txt':'14F4A15ABF92BF4866BE9FD8A4A00338',
                '16.pdf':'451B25E4AD13B671192F4E6548C1B632',
                '17.rar':'F63CEC86FF2877FAA6383C28C6F3B7EF',
                '18.rar':'57555CC3B82EDD8734558372CBB50BC9',
                '20180911.txt':'9B7C71319B104F5DD30545C1B1C711E4'
                }                

def save_file(fn, data, msdir="inbox"):
    filename = tmp_dir + fn + '_' + msdir + '_python.eml'
    f = open(filename, 'wb')  # 注意一定要用wb来打开文件,因为附件一般都是二进制文件
    f.write(data)
    f.close()
    #
    fileMd5 = GetFileMd5(filename)
    ret_str = ''
    ret_str = 'FAIL'
    if fileMd5 is None:
        writelog("file md5 get fail    =>filename:%s"%filename)
    else:
        #filename 是bytes类型  需要解码 才能变为字符串
        if  filename not in attr_md5_dic:
            writelog("file not in attr_md5_dic  =>filename:%s"%filename)
        else:
            if fileMd5 in attr_md5_dic[ filename]:
                ret_str = 'OK'
    #输出md5 list
    f = open('E:\\imap-client\\tmp\\python\\md5_list', 'a')  
    t=time.strftime( ISOTIMEFORMAT, time.localtime())   
    #f.write("Md5 of"+ filename +"is " + fileMd5+ "\r\n")
    f.write("%s Md5 of %s is %s result is %s\n"%(t, filename, fileMd5, ret_str))
    #md5content = t + " Md5 of " + filename + "is" + (fileMd5) 
    #f.write( md5content )
    f.close()
    return None
#取信息编码
def get_charset(message, default="ascii"):
    #Get the message charset
    return message.get_charset()
    return default
#解析邮件
def ParseMail(mail):

    try:
        textplain = ''
        texthtml = ''
        atta = {}
        fileMd5= ''
        
        if mail.is_multipart():
            for par in mail.walk():
                if not par.is_multipart():  # 这里要判断是否是multipart,是的话,里面的数据是无用的,至于为什么可以了解mime相关知识。
                    #name = par.get_param("name")  # 如果是附件,这里就会取出附件的文件名
                    name = par.get_filename()
                    if name:
                        # 有附件
                        # 下面的三行代码只是为了解码象=?gbk?Q?=CF=E0=C6=AC.rar?=这样的文件名
                        #print("filename:" + filename)
                        h = email.header.Header(name)
                        dh = email.header.decode_header(h)
                        fname = dh[0][0]
                        #print(dh)
                        print("fname:" + fname.decode())
                        # print '有附件'+fname
                        data = par.get_payload(decode=True)  # 解码出附件数据,然后存储到文件中
                        atta[fname] = data
                        try:
                            f = open(fname, 'wb')  # 注意一定要用wb来打开文件,因为附件一般都是二进制文件
                        except:
                            print('附件名有非法字符,自动换一个' + fname.decode())
                            f = open('aaaa', 'wb')
                        f.write(data)
                        f.close()
                        
                        fileMd5 = GetFileMd5(fname)
                        os.remove(fname) #del attr
                        if fileMd5 is None:
                            writelog("file md5 get fail    =>fname:%s"%fname)
                            return (None, None, None)
                        else:
                            #fname 是bytes类型  需要解码 才能变为字符串
                            if  fname.decode() not in attr_md5_dic:
                                writelog("file not in attr_md5_dic  =>fname:%s"%fname.decode())
                                return (None, None, None)
                            #writelog("md5:%s   md5:%s"%(fileMd5.upper() ,attr_md5_dic[ fname.decode()]) )
                            if fileMd5.upper() not in attr_md5_dic[ fname.decode()]:
                                return (None, None, None)
                        
                    else:
                        # 不是附件,是文本内容
                        content_type = par.get_content_type()
                        charset = get_charset(par)
                        if content_type in ['text/plain']:
                            if charset == None:
                                textplain = par.get_payload(decode=True)
                            else:
                                textplain = par.get_payload(decode=True).decode(charset)
                        if content_type in ['text/html']:
                            if charset == None:
                                texthtml = par.get_payload(decode=True)
                            else:
                                texthtml = par.get_payload(decode=True).decode(charset)
                        
                                         
        else:
            
            type = mail.get_content_charset()
            if type == None:
                
                textplain = mail.get_payload()
            else:
                content_type = mail.get_content_type()
                charset = get_charset(mail)
                if content_type in ['text/plain']:
                    if charset == None:
                        textplain = mail.get_payload(decode=True)
                    else:
                        textplain = mail.get_payload(decode=True).decode(charset)
        
        return (textplain, texthtml, atta)
    except Exception as e:
        writelog("error parse mail=>"+str(e))
        
        return (None, None, None)    

def imap_get_mail_body(IMAPSever,IMAPPort,IMAPUser,IMAPPassword,BaseNum,StepNum):
    #M = imaplib.IMAP4_SSL(IMAPSever, IMAPPort) 
    M = imaplib.IMAP4(IMAPSever, IMAPPort)
    M.login(IMAPUser, IMAPPassword)
    result, msg = M.select('inbox')

    #print('result %s msg is %s\n' %(result, msg))

    #typ, data = M.search(None, 'ALL')

    #print('search result is [%s]--type is [%s]\n' %(data,typ))
    #typ, maildata = M.fetch('13', '(RFC822)')
    #save_file(str(int('13')), maildata[0][1])
    #print('Message %s\n%s\n' %('1', maildata[0][1]))
    #exit(0)
    success_num = 0
    fail_num = 0

    #for num in data[0].split():
    for num in range(BaseNum,BaseNum+StepNum):
        #if( int(num) > 3 ):
        #    exit(0)
        print('Message [%s]\n' %(str(num)))
        typ, maildata = M.fetch(str(num), '(UID BODY[1])')
        #print(maildata)
        #save_file(str(int(num)), maildata[0][1])
        mail = Parser().parsestr(maildata[0][1].decode('utf-8'))
        TextContent,HTMLContent,MailAtta = ParseMail(mail)
        
        if MailAtta != None and TextContent != None and HTMLContent != None:
            success_num += 1
            
        else:
            fail_num += 1
            
        writelog('success:%d  fail:%d'% (success_num, fail_num) )    
        #print('Message [%s]\n[%s]\n' %(num, maildata[0][1]))
    #writelog('success:%d  fail:%d'% (success_num, fail_num) )
    t=time.strftime( ISOTIMEFORMAT, time.localtime()) 
    writelog('----------------imapClient.quit---------------------now time is[%s]'%t)    
    M.close()
    M.logout()


BaseNum=1
for num in range(1,30):
    imap_get_mail_body(IMAPSever,IMAPPort,IMAPUser,IMAPPassword,BaseNum,160000)
    BaseNum+=5000