def parse_mail(self): self.resp_mode = 'begin' self.wfile.write("220 CymSMTP At your Service!\r\n") self.message = SMTPMessage() self.message.set_client_address(self.client_address) try: self.raw_requestline = self.rfile.readline()[0:-2] # CRLF while self.raw_requestline: #print self.raw_requestline if self.raw_requestline == 'QUIT': return if self.resp_mode == 'begin': if not self.handle_begin(): return elif self.resp_mode == 'mailfrom': if not self.handle_mail(): return elif self.resp_mode == 'rcptto': if not self.handle_rcpto(): return elif self.resp_mode == 'data': if not self.handle_data(): return else: self.respond(500, 'Error!!!') return self.raw_requestline = self.rfile.readline()[0:-2] #CRLF except: raise
class SMTPRequestHandler(Object): rbufsize = -1 wbufsize = 0 # The Python system version, truncated to its first component. sys_version = "Python/" + sys.version.split()[0] # The server software version. You may want to override this. # The format is multiple whitespace-separated strings, # where each string is of the form name[/version]. server_version = "BaseSMTP/" + __version__ def __init__(self, server): Object.__init__(self) self.server = server sys.exc_traceback = None # Help garbage collection def awake(self, request, client_address): self.request = request self.client_address = client_address def setup(self): self.connection = self.request self.rfile = self.connection.makefile('rb', self.rbufsize) self.wfile = self.connection.makefile('wb', self.wbufsize) def finish(self): self.wfile.flush() self.wfile.close() self.rfile.close() def respond(self, code, message): self.wfile.write(`code` + " " + message + "\r\n") def handle_begin(self): s = self.raw_requestline.split(' ') if s[0] == 'HELO' or s[0] == 'EHLO': self.respond(250, "Hello " + s[1]) self.resp_mode = 'mailfrom' return True else: self.respond(500, "I didn't see what I was expecting") return False def handle_mail(self): s = self.raw_requestline.split(':') if s[0] == 'MAIL FROM': self.respond(250, "Ok") self.resp_mode = 'rcptto' self.message.set_mailfrom(s[1]) return True else: self.respond(500, "Yay, garbage!") return False def handle_rcpto(self): s = self.raw_requestline.split(':') if s[0] == 'RCPT TO': self.respond(250, "Ok") self.resp_mode = 'data' self.message.set_rcptto(s[1]) return True elif s[0] == 'QUIT': self.respond(250, "Ok") return False else: self.respond(500, "Yay, garbage!") return False def handle_data(self): digest = md5.new() if self.raw_requestline == 'DATA': self.respond(354, "End data with <CR><LF>.<CR><LF>") r = self.rfile.readline() while r: rt = r[0:-2] if rt == "." or rt == "\x04": self.respond(250, "Ok. Queued as " + digest.hexdigest()) self.message.set_id(digest.hexdigest()) self.resp_mode = 'rcptto' return True digest.update(rt) self.message.add_message_line(rt) # store message contents r = self.rfile.readline() elif self.raw_requestline.split(":")[0] == "RCPT TO": return self.handle_rcpto() else: return False def handle(self): """ Override this method if you want to do something very different with mail handling, but would still like to call the parse_mail() method to do the message setup. """ self.parse_mail() # Do something with the message here return def parse_mail(self): self.resp_mode = 'begin' self.wfile.write("220 CymSMTP At your Service!\r\n") self.message = SMTPMessage() self.message.set_client_address(self.client_address) try: self.raw_requestline = self.rfile.readline()[0:-2] # CRLF while self.raw_requestline: #print self.raw_requestline if self.raw_requestline == 'QUIT': return if self.resp_mode == 'begin': if not self.handle_begin(): return elif self.resp_mode == 'mailfrom': if not self.handle_mail(): return elif self.resp_mode == 'rcptto': if not self.handle_rcpto(): return elif self.resp_mode == 'data': if not self.handle_data(): return else: self.respond(500, 'Error!!!') return self.raw_requestline = self.rfile.readline()[0:-2] #CRLF except: raise def version_string(self): """Return the server software version string.""" return self.server_version + ' ' + self.sys_version def date_time_string(self): """Return the current date and time formatted for a message header.""" now = time.time() year, month, day, hh, mm, ss, wd, y, z = time.gmtime(now) s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( self.weekdayname[wd], day, self.monthname[month], year, hh, mm, ss) return s def log_date_time_string(self): """Return the current time formatted for logging.""" now = time.time() year, month, day, hh, mm, ss, x, y, z = time.localtime(now) s = "%02d/%3s/%04d %02d:%02d:%02d" % ( day, self.monthname[month], year, hh, mm, ss) return s weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] def address_string(self): """Return the client address formatted for logging. This version looks up the full hostname using gethostbyaddr(), and tries to find a name that contains at least one dot. """ host, port = self.client_address return socket.getfqdn(host)