[Twisted-Python] Help with SMTP Proxy

Jan Van Uytven wyvern at crm3.com
Fri Apr 22 20:19:03 EDT 2005


Hi,

I'm a new user of Twisted, I came to it because I need to write an SMTP 
proxy and want to use Python to do it. The proxy would copy the incoming 
e-mail (including the envelope headers) and then forward it to Postfix.

Googling around I came across an example of an SMTP server (written by 
the author of twisted.mail I think)  and after playing around with it a 
bit I can get it to receive and copy the e-mail, but I'm not quite sure 
how to forward it on after that. Right now I have the proxy sitting on 
port 2025 and the final smtp server on port 2030. Here's the code I have 
so far:

import os, StringIO
from zope.interface import implements
from twisted.application import service

application = service.Application("Postfix SMTP Proxy for CopyUser")

from twisted.application import internet
from twisted.internet import protocol, defer

smtpServerFactory = protocol.ServerFactory()

from twisted.mail import smtp

class FileMessage(object):
    implements(smtp.IMessage)

    def __init__(self, fileObj):
        self.fileObj = fileObj
        self.msg = []

    def lineReceived(self, line):
        self.fileObj.write(line + '\n')
        self.msg.append(line)
       
    def eomReceived(self):
        self.fileObj.close()
        print "\n".join(self.msg)
        return defer.succeed(None)
       
    def connectionLost(self):
        self.fileObj.close()
        os.remove(self.fileObj.name)

class Delivery(object):
    implements(smtp.IMessageDelivery)
    counter = 0

    def validateTo(self, user):
        fileName = 'smtplog.' + str(self.counter)
        self.counter += 1
        return lambda: FileMessage(file(fileName, 'w'))
   
    def validateFrom(self, helo, origin):
        return origin

    def receivedHeader(self, helo, origin, recipients):
        recpts = []
        for i in recipients:
            recpts.append(str(i))
        recptstr = "Recipients: " + ",".join(recpts)
        origin = "Origin: " + str(origin)
        return recptstr+"\r\n"+origin+"\r\n\r\n"

class ESMTPFactory(protocol.ServerFactory):
    protocol = smtp.ESMTP

    def buildProtocol(self, addr):
        p = self.protocol()
        p.delivery = Delivery()
        p.factory = self
        return p

class ForwardSMTPClient(smtp.ESMTPClient):

    def getMailFrom(self):
        return ["smtp_proxy at localhost"]

    def getMailTo(self):
        return "tester at localhost"

    def getMailData(self):
        data = """
        Subject: Test!

        Test!
        """
        return StringIO.StringIO(data)
   
    def sentMail(self, code, resp, numOk, addresses, log):
        print 'Sent', numOk, 'messages'


class SMTPClientFactory(protocol.ClientFactory):
    protocol = ForwardSMTPClient

    def buildProtocol(self, addr):
        return self.protocol(secret=None, identity='localhost')
       

smtpServerFactory = ESMTPFactory()
smtpClientFactory = SMTPClientFactory()
smtpServerService = internet.TCPServer(2025, smtpServerFactory)
smtpServerService.setServiceParent(application)

I've left out actually forwarding the message for now, I just want to 
forward a test e-mail upon receipt of the message. The whole structure 
of this program doesn't seem right to me. I thought initially of trying 
something like adding this to the eomReceived method:

smtpForwardService = internet.TCPClient('localhost', 2030, 
smtpClientFactory)
smtpForwardService.setServiceParent(application)

but it started an endless loop of sending the test e-mail. Most clients 
use reactor.stop() to finish after processing, but I need the reactor to 
keep on going.

Documentation on twisted.mail is almost non-existent. Is there a better 
way of doing this?

Thanks,

Still Struggling




More information about the Twisted-Python mailing list