SmtpXmpp Transport

I wrote Smtp-Xmpp Transporter by python.
Anybody need this?

This implementation has a security issue.
Only AUTH PLAIN is supported.

set your jabber server information
130: self.__xmpp_domain = 'xmppserver'
131: self.__xmpp_server = '192.168.1.1'
132: self.__xmpp_port = 5222

----------

# coding: UTF-8
import smtpd
import base64
import xmpp

""" by t.yanagiwara 2010 Feb 21
"""

__all__ = ['SmtpXmppGw', 'SMTPXMPPGateway']
__version__ = 'smtp-xmppGW 0.1';

class ESMTPServer(smtpd.SMTPServer):
    def handle_accept(self):
        """Override
        """
        conn, addr = self.accept()
        print >> smtpd.DEBUGSTREAM, 'Incoming connection from %s' % repr(addr)
        channel = ESMTPChannel(self, conn, addr)
       
    def process_auth(self, username, password):
        raise NotImplementedError

    def disconnect(self):
        pass

class ESMTPChannel(smtpd.SMTPChannel):
    AUTH = 2

    def __init__(self, server, conn, addr):
        smtpd.asynchat.async_chat.__init__(self, conn)
        self.__server = server
        self._SMTPChannel__server = server
        self._SMTPChannel__conn = conn
        self._SMTPChannel__addr = addr
        self._SMTPChannel__line = []
        self._SMTPChannel__state = self.COMMAND
        self._SMTPChannel__greeting = 0
        self._SMTPChannel__mailfrom = None
        self._SMTPChannel__rcpttos = []
        self._SMTPChannel__data = ''
        self._SMTPChannel__fqdn = smtpd.socket.getfqdn()
        self._SMTPChannel__peer = conn.getpeername()
        self.__username = None
        self.__password = None
        self.__auth = 0
        print >> smtpd.DEBUGSTREAM, 'Peer:', repr(self._SMTPChannel__peer)
        self.push('220 %s ESMTP %s' % (self._SMTPChannel__fqdn, __version__))
        self.set_terminator('\r\n')

    def found_terminator(self):
        if self._SMTPChannel__state != self.AUTH:
            return smtpd.SMTPChannel.found_terminator(self)
        line = smtpd.EMPTYSTRING.join(self._SMTPChannel__line)
        print >> smtpd.DEBUGSTREAM, 'Data:', repr(line)
        self._SMTPChannel__line = []
        self.__auth_PLAIN(line)

    def smtp_EHLO(self, arg):
        if not arg:
            self.push('501 Syntax: EHLO hostname')
            return
        if self._SMTPChannel__greeting:
            self.push('503 Duplicate HELO/EHLO')
        else:
            self._SMTPChannel__greeting = arg
            self.push('250-AUTH PLAIN')
            self.push('250 AUTH=PLAIN')

    def smtp_MAIL(self, arg):
        if not self.__username:
            self.push('503 Error: need AUTH command')
            return
        smtpd.SMTPChannel.smtp_MAIL(self, arg)

    def smtp_RSET(self, arg):
        if arg:
            self.push('501 Syntax: RSET')
            return
        self.__username = None
        self.__password = None
        self.__server.disconnect()
        smtpd.SMTPChannel.smtp_RSET(self, arg)

    def smtp_QUIT(self, arg):
        self.__server.disconnect()
        smtpd.SMTPChannel.smtp_QUIT(self, arg)

    def __auth_PLAIN(self, arg):
        self._SMTPChannel__state = self.COMMAND
        try:
            decstring = base64.standard_b64decode(arg.strip())
        except:
            self.push('535 Error: authentication failed: bad protocol / cancel')
            return
       
        args = decstring.split('\0')
        if len(args) != 3:
            self.push('535 Error: authentication failed: bad protocol / cancel')
            return
        self.__username = args[1]
        self.__password = args[2]

        #print (' %s %s ' % ( self.__username, self.__password ))
        status = self.__server.process_auth(self.__username, self.__password)
        if not status:
            self.push('235 Authentication successful')
        else:
            self.push(status)
            self.__username = None
            self.__password = None

    def smtp_AUTH(self, arg):
        if not arg:
            self.push('501 Syntax: AUTH PLAIN xxxx')
            return
        else:
            args = arg.split()
            if args[0].upper() != 'PLAIN':
                self.push('535 Error: authentication failed: no mechanism available')
                return
           
            if len(args) == 1:
                self._SMTPChannel__state = self.AUTH
                self.push('334 ')
                return
            return self.__auth_PLAIN(args[1])

class XMPPServer(ESMTPServer):
    def process_auth(self, username, password):
        self.__xmpp_domain = 'xmppserver'
        self.__xmpp_server = '192.168.1.1'
        self.__xmpp_port = 5222
       
        jid=xmpp.protocol.JID(username)
        if self.__xmpp_domain:
            cl=xmpp.Client(self.__xmpp_domain, debug=[])
            con=cl.connect(server=(self.__xmpp_server, self.__xmpp_port))
        else:
            cl=xmpp.Client(jid.getDomain(), debug=[])
            con=cl.connect()
        if not con:
            cl.disconnect()
            return '535 Error: authentication failed / internal server error'
        print 'connected with',con
       
        if jid.getNode():
            auth=cl.auth(jid.getNode(), password, resource="Gateway") #jid.getResource())
        else:
            auth=cl.auth(username, password, resource="Gateway") #jid.getResource())
        print auth
        if not auth:
            cl.disconnect()
            return '535 Error: authentication failed'
        self.__client = cl

    def process_message(self, peer, mailfrom, rcpttos, data):
        if not self.__client:
            return '550 Error: internal server error'
        for dest in rcpttos:
            id=self.__client.send(xmpp.protocol.Message(dest, data))

    def disconnect(self):
        if self.__client:
            self.__client.disconnect()
        self.__client = None

if __name__ == '__main__':
    myserver = XMPPServer(('0.0.0.0', 25), None)
    try:
        smtpd.asyncore.loop()
    except KeyboardInterrupt:
        pass
    except:
        raise

Syndicate content