[Twisted-Python] Simple multiplex-relayer with

Jp Calderone exarkun at divmod.com
Fri Nov 26 12:57:35 EST 2004


On Fri, 26 Nov 2004 11:04:44 +0200, bostik at 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




More information about the Twisted-Python mailing list