Mini SMTP Server
From Noah.org
This demonstrates a mini SMTP server.
Click here to download: minismtpd.py
#!/usr/bin/env python """ SYNOPSIS minismtpd.py [-h] [-v,--verbose] [--version] hostname port Maildir_path DESCRIPTION This is a mini mail server (SMTP daemon). All mail received will be written to a Maildir style mail spool. This simply receives all mail and saves it to a common Maildir spool no matter which user the mail is addressed to. This may be ideal for a single user system or this may not be what you want. EXAMPLES minismtpd.py localhost 25 /tmp/Maildir AUTHOR Noah Spurrier <noah@noah.org> LICENSE This script is in the public domain, free from copyrights or restrictions. VERSION $Id: minismtpd.py 248 2008-04-30 01:04:08Z noah $ """ import sys, os, threading, traceback, optparse import time import asyncore, smtpd, mailbox, email from email.Utils import COMMASPACE, formatdate class SMTPServerMaildir (smtpd.SMTPServer, threading.Thread): """This may seem a little bit confusing if you are not familiar with asyncore because it will seem as if nothing in this application will ever call process_message. But SMTPServer inherits from asyncore.dispatcher, and so will insert itself into asyncore's event loop on instantiation.""" def __init__ (self, host = "localhost", port = 25, Maildir_path=None, name=None): localaddr = (host, port) remoteaddr = None smtpd.SMTPServer.__init__ (self, localaddr, remoteaddr) if name is None: name = SMTPServerMaildir.__class__ threading.Thread.__init__ (self, name = name) self.open_Maildir (Maildir_path) # this sets self.inbox self.finished = threading.Event () def open_Maildir (self, Maildir_path): # TODO this is a little scruffy. Should be cleaner I think. if os.path.isdir (Maildir_path): self.inbox = mailbox.Maildir (Maildir_path, None) else: if not os.path.isfile (Maildir_path): self.inbox = mailbox.Maildir (Maildir_path, None, create=True) else: raise Exception ("Maildir exists and is not readable.") # os.makedirs(dts_path) # os.makedirs(os.path.join(dts_path, 'cur')) # os.makedirs(os.path.join(dts_path, 'new')) # os.makedirs(os.path.join(dts_path, 'tmp')) def process_message (self, peer, mailfrom, rcpttos, data): m = email.message_from_string (data) m ['From'] = mailfrom m ['To'] = COMMASPACE.join(rcpttos) if 'Date' not in m: m ['Date'] = formatdate(localtime=True) print 'To:', m['To'] if self.inbox is not None: self.inbox.add (m) def stop (self, timeout=None): self.finished.set () threading.Thread.join (self, timeout) self.SMTPServer.close () def run (self): while not self.finished.isSet (): asyncore.loop (timeout=0.25, count=1) def main (): global options, args host = args [0] port = int(args [1]) Maildir_path = args [2] SMTPd = SMTPServerMaildir (host=host, port=port, Maildir_path = Maildir_path) SMTPd.start () try: while True: time.sleep (0.25) except KeyboardInterrupt, e: # Ctrl-C SMTPd.stop () raise e except SystemExit, e: # sys.exit () SMTPd.stop () raise e if __name__ == '__main__': try: start_time = time.time () parser = optparse.OptionParser (formatter=optparse.TitledHelpFormatter (), usage=globals ()['__doc__'], version='$Id: minismtpd.py 248 2008-04-30 01:04:08Z noah $') parser.add_option ('-v', '--verbose', action='store_true', default=False, help='verbose output') (options, args) = parser.parse_args () if len (args) < 2 or len (args) > 3: parser.error ('incorrect number of arguments. Need Maildir_path and hostname.') if options.verbose: print time.asctime () main () if options.verbose: print time.asctime () if options.verbose: print 'TOTAL TIME IN MINUTES:', if options.verbose: print (time.time () - start_time) / 60.0 sys.exit (0) except KeyboardInterrupt, e: # Ctrl-C raise e except SystemExit, e: # sys.exit () raise e except Exception, e: print 'ERROR, UNEXPECTED EXCEPTION' print str (e) traceback.print_exc () os._exit (1) # vi:ts=4:sw=4:expandtab:ft=python: