--- smtpd.py.orig 2005-10-28 20:07:40.000000000 +0900 +++ smtpd.py 2008-04-01 15:03:44.273600000 +0900 @@ -78,6 +78,8 @@ import socket import asyncore import asynchat +import types +from cStringIO import StringIO __all__ = ["SMTPServer","DebuggingServer","PureProxy","MailmanProxy"] @@ -106,6 +108,7 @@ class SMTPChannel(asynchat.async_chat): + THRESHOLD = 1024*1024 COMMAND = 0 DATA = 1 @@ -114,12 +117,11 @@ self.__server = server self.__conn = conn self.__addr = addr - self.__line = [] + self.__line = StringIO() self.__state = self.COMMAND self.__greeting = 0 self.__mailfrom = None self.__rcpttos = [] - self.__data = '' self.__fqdn = socket.getfqdn() self.__peer = conn.getpeername() print >> DEBUGSTREAM, 'Peer:', repr(self.__peer) @@ -132,14 +134,39 @@ # Implementation of base class abstract method def collect_incoming_data(self, data): - self.__line.append(data) + self.__line.write(data) + + # avoid huge memory usage + if type(self.__line) != types.FileType and self._fplen(self.__line) > self.THRESHOLD: + newline = os.tmpfile() + self.__line.seek(0) + newline.write(self.__line.read()) + self.__line.close() + self.__line = None + self.__line = newline + + def _fplen(self,fp): + cur = fp.tell() + fp.seek(os.SEEK_END) + ret = fp.tell() + fp.seek(cur) + return ret + + def _fpstr(self,fp): + cur = fp.tell() + fp.seek(0) + ret = fp.read() + fp.seek(cur) + return ret # Implementation of base class abstract method def found_terminator(self): - line = EMPTYSTRING.join(self.__line) - print >> DEBUGSTREAM, 'Data:', repr(line) - self.__line = [] + linefp = self.__line + linefp.seek(0) + print >> DEBUGSTREAM, 'Data:', repr(self._fpstr(linefp)) + self.__line = StringIO() if self.__state == self.COMMAND: + line = self._fpstr(linefp) if not line: self.push('500 Error: bad syntax') return @@ -161,19 +188,35 @@ if self.__state != self.DATA: self.push('451 Internal confusion') return + # Remove extraneous carriage returns and de-transparency according # to RFC 821, Section 4.5.2. - data = [] - for text in line.split('\r\n'): - if text and text[0] == '.': - data.append(text[1:]) + datafp = None + if self._fplen(linefp) > self.THRESHOLD: + datafp = os.tmpfile() + else: + datafp = StringIO() + for text in linefp: + if text and text[0]=='.': + datafp.write(text[1:]) else: - data.append(text) - self.__data = NEWLINE.join(data) - status = self.__server.process_message(self.__peer, - self.__mailfrom, - self.__rcpttos, - self.__data) + datafp.write(text) + linefp.close() + linefp = None + datafp.seek(0) + + status = None + if 'process_message_huge' in dir(self.__server): + status = self.__server.process_message_huge(self.__peer, + self.__mailfrom, + self.__rcpttos, + datafp) + else: + status = self.__server.process_message(self.__peer, + self.__mailfrom, + self.__rcpttos, + datafp.read()) + datafp.close() self.__rcpttos = [] self.__mailfrom = None self.__state = self.COMMAND @@ -252,7 +295,6 @@ # Resets the sender, recipients, and data, but not the greeting self.__mailfrom = None self.__rcpttos = [] - self.__data = '' self.__state = self.COMMAND self.push('250 Ok')