On Fri, 26 Nov 2004 11:04:44 +0200, bostik@stinghorn.com (Mika Bostrom) wrote:
Good day, hackers.
I'm trying to implement a rather simple, localhost-bound mail relay with Twisted. The setup is follows:
[snip]
Code:
[--snip--] #!/usr/bin/python
from twisted.internet import reactor, protocol, defer from twisted.protocols import smtp from twisted.python import log import sys
class RelayUtility: """Utility class for holding runtime values"""
def __init__(self): self.maxconns =3D 20 self.active =3D 0
class RelayMessage(smtp.IMessage): def __init__(self): smtp.IMessage.__init__(self) self.msg =3D []
The above class is the most obvious problem I see here. Interfaces are not meant to be subclassed in this manner. What you really want is something more like: class RelayMessage: __implements__ = smtp.IMessage def lineReceived(self, line): # Do something with the line; perhaps buffer it in memory, # perhaps try and send it to another connection. def eomReceived(self): # The message has been fully received; flush the buffer or take # whatever other action is appropriate to ensure message delivery. # Return a Deferred that fires when the message has been successfully # delivered. def connectionLost(self): # Discard message content, delivery is a failure
class RelayProtocol(smtp.ESMTP): """Relayer; sucks the mail in"""
def __init__(self): self.util =3D util # Normal operations smtp.ESMTP.__init__(self) self.host =3D "nowhere.dot.invalid"
def connectionLost(self, reason): self.util.active -=3D 1
def connectionMade(self): # The easiest way. Increments upon connection, decrements # upon disconnection; In case of full queue, just kick the client self.util.active +=3D 1 if (self.util.active <=3D self.util.maxconns): smtp.ESMTP.connectionMade(self) else: self.sendCode(430, "Queue full. Try again later.") self.transport.loseConnection()
# This can't be right def validateFrom(self, helo, origin): return smtp.Address(origin, None)
# This is certainly not right, DATA barks def validateTo(self, user): return RelayMessage
You _could_ do things this way, but a preferable way is probably: class RelayDeliveryFactory: __implements__ = smtp.IMessageDeliveryFactory def getMessageDelivery(self): return RelayDelivery() class RelayDelivery: __implements__ = smtp.IMessageDelivery def receivedHeader(self, helo, origin, recipients): return "Received: something" def validateFrom(self, helo, origin): return origin def validateTo(self, user): return RelayMessage
class RelayFactory(smtp.SMTPFactory): protocol =3D RelayProtocol
Then add this buildProtocol method: def buildProtocol(self, addr): p = smtp.SMTPFactory.buildProtocol(self, addr) p.deliveryFactory = RelayDeliveryFactory() return p ESMTP will call getMessageDelivery on its deliveryFactory attribute, now that it isn't None. On the object it returns, it will call receivedHeader, validateFrom, and validateTo. And on the object returned by calling the object returned by validateTo, it will pass the contents of the message being delivered, letting you relay it wherever is appropriate. Hope this helps, Jp