import asynchat, asyncore, socket import pwd, os, sys, logging, logging.handlers import mailbox, email class SMTPChannel(asynchat.async_chat): def __init__(self, server, sock, addr): asynchat.async_chat.__init__(self, sock) self.server = server self.set_terminator("\n") self.data = "" self.read_data = False self.push("220 Mailserver terrorist.at. Nutzung ohne vorige Erlaubnis verboten!\n") def collect_incoming_data(self, data): if len(self.data) > 0: self.data += "\n" self.data += data self.server.logger.debug("Read: " + data.strip()) if self.data.__len__() > 16384: self.server.logger.error("too much data, shutdown") self.close_when_done() def push(self,data): self.server.logger.debug("Write: " + data) asynchat.async_chat.push(self,data) def log(self, message): self.server.logger.info(message) def log_info(self, message, type='info'): self.server.logger.error("%s %s" % (type, message)) def handle_close(self): asynchat.async_chat.handle_close(self) if self.read_data and len(self.data) > 10: #QUIT oder "." wurde nicht erkannt mail = email.message_from_string(self.data) key=self.server.maildir.add(mail) self.server.logger.info("New Mail: %s" % key) self.server.maildir.flush() def found_terminator(self): try: if self.read_data: if self.data.endswith("\n.") or self.data.endswith("\n.\r"): self.read_data = False self.push("250 Ok: queued as 12345\n") mail = email.message_from_string(self.data) key=self.server.maildir.add(mail) self.server.logger.info("New Mail: %s" % key) self.server.maildir.flush() self.data = "" else: pass elif self.data.startswith("EHLO") \ or self.data.startswith("HELO"): self.push("250 Mailserver terrorist.at. Nutzung ohne vorige Erlaubnis verboten!\n") self.data = "" elif self.data.startswith("DATA"): self.push("354 End data with .\n") self.read_data = True self.data="" elif self.data.startswith("QUIT"): self.data = "" self.push("221 Bye\n") self.close_when_done() else: self.push("250 OK\n") self.data = "" except Exception,e: self.server.logger.error(str(e)) class SMTPServer(asyncore.dispatcher): def __init__(self,interface,maildir_dir): asyncore.dispatcher.__init__(self) self.port = 25 self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind((interface, self.port)) self.listen(5) self.maildir = mailbox.Maildir(maildir_dir, create=True) self.logger = logging.getLogger('smtplogger') self.logger.setLevel(logging.DEBUG) handler = logging.handlers.TimedRotatingFileHandler("log/smtp.log", "D", 1) self.logger.addHandler(handler) def handle_accept(self): conn, addr = self.accept() try: self.logger.info("New Client %s:%s" % conn.getpeername()) SMTPChannel(self, conn, addr) except Exception,e: self.logger.error(str(e)) if __name__ == '__main__': import getopt def usage(): print """smtp.py -d debug -f foreground (no fork, no user changing) -u USER setuserid to USER, else "nobody", see -f -m MAILDIR set maildir path, else "Maildir/" -s ADDR Bind to interface with ip ADDR, else 0.0.0.0 -h help""" try: opts, args = getopt.getopt(sys.argv[1:], "dfu:s:m:h") except getopt.GetoptError, err: print str(err) usage(); sys.exit(2) debug = False foreground = False user = "nobody" maildir_dir = "Maildir" interface = "0.0.0.0" for o, a in opts: if o == "-d": debug = True elif o == "-f": foreground = True elif o == "-u": user = a elif o == "-m": maildir_dir = a elif o == "-s": interface = a elif o == "-h": usage() sys.exit(0); else: assert False, "unhandled option:" + o s = SMTPServer(interface, maildir_dir) if not foreground: #user change pwinfo = pwd.getpwnam(user) os.setregid(pwinfo[3],pwinfo[3]) os.setreuid(pwinfo[2],pwinfo[2]) #"Robustly turn into a UNIX daemon, running in our_home_dir." # First fork try: if os.fork() > 0: sys.exit(0) # kill off parent except OSError, e: sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror)) sys.exit(1) os.setsid() os.chdir('/') os.umask(0) # Second fork try: pid = os.fork() if pid > 0: os._exit(0) except OSError, e: sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror)) os._exit(1) si = open('/dev/null', 'r') so = open('/dev/null', 'a+', 0) se = open('/dev/null', 'a+', 0) os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) # Set custom file descriptors so that they get proper buffering. sys.stdout, sys.stderr = so, se #""" s.logger.info("Startup; pid=%s" % os.getpid()) asyncore.loop()