summaryrefslogtreecommitdiffstats
path: root/Lib/smtpd.py
diff options
context:
space:
mode:
authorR David Murray <rdmurray@bitdance.com>2014-06-11 15:18:08 (GMT)
committerR David Murray <rdmurray@bitdance.com>2014-06-11 15:18:08 (GMT)
commit554bcbf1b925399ef06dd9fd6b87a20f5d028c1e (patch)
treefa7bc0ec474895b0051422d855065960eb68c3ca /Lib/smtpd.py
parent38ee9afb34d8eb3fc7d080c447f245ab8369934d (diff)
downloadcpython-554bcbf1b925399ef06dd9fd6b87a20f5d028c1e.zip
cpython-554bcbf1b925399ef06dd9fd6b87a20f5d028c1e.tar.gz
cpython-554bcbf1b925399ef06dd9fd6b87a20f5d028c1e.tar.bz2
#19662: add decode_data to smtpd so you can get at DATA in bytes form.
Otherwise smtpd is restricted to 7bit clean data, since even if the incoming data is actually utf-8, it will often break things to decode it before parsing the message. Patch by Maciej Szulik, with some adjustments (mostly the warning support).
Diffstat (limited to 'Lib/smtpd.py')
-rwxr-xr-xLib/smtpd.py45
1 files changed, 36 insertions, 9 deletions
diff --git a/Lib/smtpd.py b/Lib/smtpd.py
index 1fa157a..569b42e 100755
--- a/Lib/smtpd.py
+++ b/Lib/smtpd.py
@@ -98,7 +98,6 @@ class Devnull:
DEBUGSTREAM = Devnull()
NEWLINE = '\n'
-EMPTYSTRING = ''
COMMASPACE = ', '
DATA_SIZE_DEFAULT = 33554432
@@ -122,12 +121,28 @@ class SMTPChannel(asynchat.async_chat):
max_command_size_limit = max(command_size_limits.values())
def __init__(self, server, conn, addr, data_size_limit=DATA_SIZE_DEFAULT,
- map=None):
+ map=None, decode_data=None):
asynchat.async_chat.__init__(self, conn, map=map)
self.smtp_server = server
self.conn = conn
self.addr = addr
self.data_size_limit = data_size_limit
+ if decode_data is None:
+ warn("The decode_data default of True will change to False in 3.6;"
+ " specify an explicit value for this keyword",
+ DeprecationWarning, 2)
+ decode_data = True
+ self._decode_data = decode_data
+ if decode_data:
+ self._emptystring = ''
+ self._linesep = '\r\n'
+ self._dotsep = '.'
+ self._newline = NEWLINE
+ else:
+ self._emptystring = b''
+ self._linesep = b'\r\n'
+ self._dotsep = b'.'
+ self._newline = b'\n'
self.received_lines = []
self.smtp_state = self.COMMAND
self.seen_greeting = ''
@@ -287,11 +302,14 @@ class SMTPChannel(asynchat.async_chat):
return
elif limit:
self.num_bytes += len(data)
- self.received_lines.append(str(data, "utf-8"))
+ if self._decode_data:
+ self.received_lines.append(str(data, 'utf-8'))
+ else:
+ self.received_lines.append(data)
# Implementation of base class abstract method
def found_terminator(self):
- line = EMPTYSTRING.join(self.received_lines)
+ line = self._emptystring.join(self.received_lines)
print('Data:', repr(line), file=DEBUGSTREAM)
self.received_lines = []
if self.smtp_state == self.COMMAND:
@@ -300,6 +318,8 @@ class SMTPChannel(asynchat.async_chat):
self.push('500 Error: bad syntax')
return
method = None
+ if not self._decode_data:
+ line = str(line, 'utf-8')
i = line.find(' ')
if i < 0:
command = line.upper()
@@ -330,12 +350,12 @@ class SMTPChannel(asynchat.async_chat):
# Remove extraneous carriage returns and de-transparency according
# to RFC 5321, Section 4.5.2.
data = []
- for text in line.split('\r\n'):
- if text and text[0] == '.':
+ for text in line.split(self._linesep):
+ if text and text[0] == self._dotsep:
data.append(text[1:])
else:
data.append(text)
- self.received_data = NEWLINE.join(data)
+ self.received_data = self._newline.join(data)
status = self.smtp_server.process_message(self.peer,
self.mailfrom,
self.rcpttos,
@@ -577,10 +597,17 @@ class SMTPServer(asyncore.dispatcher):
channel_class = SMTPChannel
def __init__(self, localaddr, remoteaddr,
- data_size_limit=DATA_SIZE_DEFAULT, map=None):
+ data_size_limit=DATA_SIZE_DEFAULT, map=None,
+ decode_data=None):
self._localaddr = localaddr
self._remoteaddr = remoteaddr
self.data_size_limit = data_size_limit
+ if decode_data is None:
+ warn("The decode_data default of True will change to False in 3.6;"
+ " specify an explicit value for this keyword",
+ DeprecationWarning, 2)
+ decode_data = True
+ self._decode_data = decode_data
asyncore.dispatcher.__init__(self, map=map)
try:
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -599,7 +626,7 @@ class SMTPServer(asyncore.dispatcher):
def handle_accepted(self, conn, addr):
print('Incoming connection from %s' % repr(addr), file=DEBUGSTREAM)
channel = self.channel_class(self, conn, addr, self.data_size_limit,
- self._map)
+ self._map, self._decode_data)
# API for "doing something useful with the message"
def process_message(self, peer, mailfrom, rcpttos, data):