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
Using pymilter with sendmail
Usingpymilter with sendmail or postfix will solve your security problem, and make the gateway much more robust. Also consider a more selective gateway. E.g., when the mail server is shared with other users it should redirect message to you only. And in my case, only messages to me from certain senders (that insist on using email for conversations that should be on IM).