import sys, smtps, string, smtplib, rfc822, StringIO,os,glob,DNS,time
# Designed to run via cron. Will check a directory and
# if any new message files are there it will send them of and
# move them to an archive sub-directory
# It uses DNS to resolve each RCPT TO:
# address, then uses smtplib to forward the client mail on the
# resolved mailhost.
class SMTPRemoteService():
    def __init__(self):
        self.dnshost="199.45.32.41" #Some dns that belongs to verizon. :)
                                    #this should work but if you want to 
                                    #use another one feel free
        self.pollDir="/path/to/your/message_directory/"
        self.archiveDir=self.pollDir+"msg_archive/"
        self.fileList=[]
        self.savedTo = []
        self.savedMailFrom = ''
        
    def mxlookup(self,host):
        global DNSHOST
        a = DNS.DnsRequest(host, qtype = 'mx', server=self.dnshost).req().answers
        l = map(lambda x:x['data'], a)
        l.sort()
        return l
        
    def mailFrom(self, args):
        # Stash who its from for later
        self.savedMailFrom = smtps.stripAddress(args)
        
    def rcptTo(self, args):
        # Stashes multiple RCPT TO: addresses
        self.savedTo.append(args)
        
    def data(self, args):
        # Process client mail data. It inserts a silly X-Header, then
        # does a MX DNS lookup for each TO address stashed by the
        # rcptTo method above. Messages are logged to the console as
        # things proceed. 
        data = self.frobData(args)
        for addressee in self.savedTo:
            toHost, toFull = smtps.splitTo(addressee)
            # Treat this TO address speciallt. All because of a
            # WINSOCK bug!
            if toFull == 'shutdown@shutdown.now':
                self.shutdown = 1
                return
            sys.stdout.write('Resolving ' + toHost + '...')
            resolved = self.mxlookup(toHost)
            if len(resolved):
                sys.stdout.write(' found. Sending ...')
                mxh = resolved[0][1]
                for retries in range(3):
                    try:
                        smtpServer = smtplib.SMTP(mxh)
                        smtpServer.set_debuglevel(0)
                        smtpServer.helo(mxh)
                        smtpServer.sendmail(self.savedMailFrom, toFull, data)
                        smtpServer.quit()
                        print(' Sent TO:', toFull, mxh)
                        break
                    except e:
                        print('*** SMTP FAILED', retries, mxh, sys.exc_info()[1])
                        continue
            else:
                print('*** NO MX HOST for :', toFull)
        self.savedTo = []

    def frobData(self, data):
        hend = string.find(data, '\n\r')
        if hend != -1:
            rv = data[:hend]
        else:
            rv = data[:]
        rv = rv + 'X-PySpy: Python SMTP Proxy Frobnication'
        rv = rv + data[hend:]
        return rv
        
    def archiveFile(self,msgfile):
       timeStamp=time.strftime("%m%d%y_%I%M%S%p",time.localtime())
       in_msg=open(msgfile, "rb")
       outfile=open(self.archiveDir+timeStamp, "wb")
       outfile.write(in_msg.read())
       outfile.close()
       in_msg.close()
       os.remove(msgfile)
       
    def run(self):
        contents=""
        inContents=False
        self.fileList=glob.glob(self.pollDir+'*.msg') 
        for filename in self.fileList:
            for line in open(filename,"r").readlines():
                if line.startswith("RCPT"):
                    self.rcptTo(line)
                if line.startswith("FROM:"):
                    self.mailFrom(line[5:])              
                if line.startswith("Message-ID"):
                    inContents=True
                if inContents:
                    contents=contents+line
            self.data(contents)
            inContents=False
            contents=""
            self.archiveFile(filename)
    
    
if __name__ == '__main__':
    service = SMTPRemoteService()
    service.run()