Mercurial > smtpserver
changeset 4:87317bd93890 default tip
The server now has a separate thread that forwards queued mail to an external server.
author | Atul Varma <varmaa@toolness.com> |
---|---|
date | Tue, 18 Mar 2008 22:25:31 -0500 |
parents | 769f41699038 |
children | |
files | .hgignore smtpserver.py |
diffstat | 2 files changed, 105 insertions(+), 14 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Tue Mar 18 21:00:41 2008 -0500 +++ b/.hgignore Tue Mar 18 22:25:31 2008 -0500 @@ -1,5 +1,6 @@ syntax: glob *.pyc +*.log *~ smtpconfig.py
--- a/smtpserver.py Tue Mar 18 21:00:41 2008 -0500 +++ b/smtpserver.py Tue Mar 18 22:25:31 2008 -0500 @@ -5,11 +5,59 @@ import asyncore import grp import pwd +import logging +import traceback +import threading +import Queue + +class MockSMTP( object ): + def __init__( self, server, port ): + pass + + def login( self, username, password ): + pass + + def sendmail( self, mailfrom, rcpttos, data ): + pass + + def quit( self ): + pass + +def mailThread( queue, config, smtpClass ): + done = False + + try: + while not done: + item = queue.get() + if item["cmd"] == "quit": + logging.info( "quit signal received, exiting." ) + done = True + elif item["cmd"] == "sendmsg": + # TODO: If the sending of the message fails, we should + # try to re-queue it and re-send it later. + logging.info( + "sending message from %s to %s" % ( item["mailfrom"], + item["rcpttos"] ) + ) + server = smtpClass( config.server, config.port ) + server.login( config.username, config.password ) + server.sendmail( item["mailfrom"], + item["rcpttos"], + item["data"] ) + server.quit() + logging.info( "done." ) + else: + raise AssertionError( "Bad cmd: %s" % item["cmd"] ) + except: + logging.error( "exception in mailThread()." ) + logging.error( traceback.format_exc() ) class MyServer( smtpd.SMTPServer ): import smtpconfig as config - def __init__( self ): + def __init__( self, smtpClass ): + self.smtpClass = smtpClass + self.queue = Queue.Queue() smtpd.SMTPServer.__init__( self, self.config.my_addr, None ) def bind( self, addr ): @@ -18,27 +66,69 @@ uid = pwd.getpwnam( self.config.uid )[2] os.setgid( gid ) os.setuid( uid ) - print "Bound to %s and changed user to %s." % ( addr, - self.config.uid ) + logging.info( + "Bound to %s and changed user to %s." % ( addr, + self.config.uid ) + ) + + self.mailThread = threading.Thread( + target = mailThread, + args = (self.queue, self.config, self.smtpClass) + ) + self.mailThread.start() + return retval + def finalize( self ): + self.queue.put( {"cmd" : "quit"} ) + def process_message( self, peer, mailfrom, rcpttos, data ): if peer[0] != "127.0.0.1": - print "not from localhost: %s" % peer + logging.warn( "not from localhost: %s" % peer ) return - print "sending message from %s to %s" % (mailfrom, rcpttos) - server = smtplib.SMTP( self.config.server, self.config.port ) - server.set_debuglevel( 1 ) - server.login( self.config.username, self.config.password ) - server.sendmail( mailfrom, rcpttos, data ) - server.quit() - print "done." + + if not self.mailThread.isAlive(): + logging.info( "mailThread is no longer alive! exiting." ) + raise asyncore.ExitNow() + + logging.info( + "queuing message from %s to %s on %s (length %d)." % + (mailfrom, rcpttos, peer, len(data)) + ) + + self.queue.put( { "cmd" : "sendmsg", + "mailfrom" : mailfrom, + "rcpttos" : rcpttos, + "data" : data } ) if __name__ == "__main__": if os.getuid() != 0: print "This program must be run as root." sys.exit( -1 ) - server = MyServer() - print "Listening for incoming connections on port %s." % \ + + logging.basicConfig( + filename = "smtpserver.log", + format = "%(asctime)-15s %(levelname)s: %(message)s", + level = logging.INFO + ) + + if len( sys.argv ) == 2 and sys.argv[1] == "test": + msg = "Test mode enabled." + logging.info( msg ) + print msg + + smtpClass = MockSMTP + else: + smtpClass = smtplib.SMTP + + server = MyServer( smtpClass ) + + msg = "Listening for incoming connections on port %s." % \ server.config.my_addr[1] - asyncore.loop() + logging.info( msg ) + print msg + + try: + asyncore.loop() + finally: + server.finalize()