blob: 6eb47f1deec73928fa419e83106e9aa02fbb0b0a [file] [log] [blame]
R David Murrayd1a30c92012-05-26 14:33:59 -04001import unittest
R David Murray2539e672014-08-09 16:40:49 -04002import textwrap
Richard Jones64b02de2010-08-03 06:39:33 +00003from test import support, mock_socket
Richard Jones8cb36192010-07-23 16:20:40 +00004import socket
Richard Jones8cb36192010-07-23 16:20:40 +00005import io
6import smtpd
Richard Jones64b02de2010-08-03 06:39:33 +00007import asyncore
Richard Jones8cb36192010-07-23 16:20:40 +00008
Richard Jones8cb36192010-07-23 16:20:40 +00009
10class DummyServer(smtpd.SMTPServer):
R David Murray2539e672014-08-09 16:40:49 -040011 def __init__(self, *args, **kwargs):
12 smtpd.SMTPServer.__init__(self, *args, **kwargs)
Georg Brandl6d23c442010-07-29 13:19:42 +000013 self.messages = []
R David Murray2539e672014-08-09 16:40:49 -040014 if self._decode_data:
R David Murray554bcbf2014-06-11 11:18:08 -040015 self.return_status = 'return status'
16 else:
17 self.return_status = b'return status'
Richard Jones64b02de2010-08-03 06:39:33 +000018
Richard Jones8cb36192010-07-23 16:20:40 +000019 def process_message(self, peer, mailfrom, rcpttos, data):
20 self.messages.append((peer, mailfrom, rcpttos, data))
R David Murray554bcbf2014-06-11 11:18:08 -040021 if data == self.return_status:
Richard Jones8cb36192010-07-23 16:20:40 +000022 return '250 Okish'
23
R David Murray2539e672014-08-09 16:40:49 -040024 def process_smtputf8_message(self, *args, **kwargs):
25 return '250 SMTPUTF8 message okish'
26
Richard Jones4aa0d4d2010-08-04 01:20:14 +000027
Richard Jones8cb36192010-07-23 16:20:40 +000028class DummyDispatcherBroken(Exception):
29 pass
30
Richard Jones4aa0d4d2010-08-04 01:20:14 +000031
Richard Jones8cb36192010-07-23 16:20:40 +000032class BrokenDummyServer(DummyServer):
33 def listen(self, num):
34 raise DummyDispatcherBroken()
35
Richard Jones4aa0d4d2010-08-04 01:20:14 +000036
R David Murrayd1a30c92012-05-26 14:33:59 -040037class SMTPDServerTest(unittest.TestCase):
Richard Jones4aa0d4d2010-08-04 01:20:14 +000038 def setUp(self):
39 smtpd.socket = asyncore.socket = mock_socket
40
41 def test_process_message_unimplemented(self):
R David Murray6fe56a32014-06-11 13:48:58 -040042 server = smtpd.SMTPServer((support.HOST, 0), ('b', 0),
43 decode_data=True)
Richard Jones4aa0d4d2010-08-04 01:20:14 +000044 conn, addr = server.accept()
R David Murray554bcbf2014-06-11 11:18:08 -040045 channel = smtpd.SMTPChannel(server, conn, addr, decode_data=True)
Richard Jones4aa0d4d2010-08-04 01:20:14 +000046
47 def write_line(line):
48 channel.socket.queue_recv(line)
49 channel.handle_read()
50
R David Murrayd1a30c92012-05-26 14:33:59 -040051 write_line(b'HELO example')
Richard Jones4aa0d4d2010-08-04 01:20:14 +000052 write_line(b'MAIL From:eggs@example')
53 write_line(b'RCPT To:spam@example')
54 write_line(b'DATA')
55 self.assertRaises(NotImplementedError, write_line, b'spam\r\n.\r\n')
56
R David Murray2539e672014-08-09 16:40:49 -040057 def test_process_smtputf8_message_unimplemented(self):
58 server = smtpd.SMTPServer((support.HOST, 0), ('b', 0),
59 enable_SMTPUTF8=True)
60 conn, addr = server.accept()
61 channel = smtpd.SMTPChannel(server, conn, addr, enable_SMTPUTF8=True)
62
63 def write_line(line):
64 channel.socket.queue_recv(line)
65 channel.handle_read()
66
67 write_line(b'EHLO example')
68 write_line(b'MAIL From: <eggs@example> BODY=8BITMIME SMTPUTF8')
69 write_line(b'RCPT To: <spam@example>')
70 write_line(b'DATA')
71 self.assertRaises(NotImplementedError, write_line, b'spam\r\n.\r\n')
72
R David Murray554bcbf2014-06-11 11:18:08 -040073 def test_decode_data_default_warns(self):
74 with self.assertWarns(DeprecationWarning):
R David Murray6fe56a32014-06-11 13:48:58 -040075 smtpd.SMTPServer((support.HOST, 0), ('b', 0))
R David Murray554bcbf2014-06-11 11:18:08 -040076
R David Murray2539e672014-08-09 16:40:49 -040077 def test_decode_data_and_enable_SMTPUTF8_raises(self):
78 self.assertRaises(
79 ValueError,
80 smtpd.SMTPServer,
81 (support.HOST, 0),
82 ('b', 0),
83 enable_SMTPUTF8=True,
84 decode_data=True)
85
86 def tearDown(self):
87 asyncore.close_all()
88 asyncore.socket = smtpd.socket = socket
89
90
91class DebuggingServerTest(unittest.TestCase):
92
93 def setUp(self):
94 smtpd.socket = asyncore.socket = mock_socket
95
96 def send_data(self, channel, data, enable_SMTPUTF8=False):
97 def write_line(line):
98 channel.socket.queue_recv(line)
99 channel.handle_read()
100 write_line(b'EHLO example')
101 if enable_SMTPUTF8:
102 write_line(b'MAIL From:eggs@example BODY=8BITMIME SMTPUTF8')
103 else:
104 write_line(b'MAIL From:eggs@example')
105 write_line(b'RCPT To:spam@example')
106 write_line(b'DATA')
107 write_line(data)
108 write_line(b'.')
109
110 def test_process_message_with_decode_data_true(self):
111 server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0),
112 decode_data=True)
113 conn, addr = server.accept()
114 channel = smtpd.SMTPChannel(server, conn, addr, decode_data=True)
115 with support.captured_stdout() as s:
116 self.send_data(channel, b'From: test\n\nhello\n')
117 stdout = s.getvalue()
118 self.assertEqual(stdout, textwrap.dedent("""\
119 ---------- MESSAGE FOLLOWS ----------
120 From: test
121 X-Peer: peer-address
122
123 hello
124 ------------ END MESSAGE ------------
125 """))
126
127 def test_process_message_with_decode_data_false(self):
128 server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0),
129 decode_data=False)
130 conn, addr = server.accept()
131 channel = smtpd.SMTPChannel(server, conn, addr, decode_data=False)
132 with support.captured_stdout() as s:
133 self.send_data(channel, b'From: test\n\nh\xc3\xa9llo\xff\n')
134 stdout = s.getvalue()
135 self.assertEqual(stdout, textwrap.dedent("""\
136 ---------- MESSAGE FOLLOWS ----------
137 b'From: test'
138 b'X-Peer: peer-address'
139 b''
140 b'h\\xc3\\xa9llo\\xff'
141 ------------ END MESSAGE ------------
142 """))
143
144 def test_process_message_with_enable_SMTPUTF8_true(self):
145 server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0),
146 enable_SMTPUTF8=True)
147 conn, addr = server.accept()
148 channel = smtpd.SMTPChannel(server, conn, addr, enable_SMTPUTF8=True)
149 with support.captured_stdout() as s:
150 self.send_data(channel, b'From: test\n\nh\xc3\xa9llo\xff\n')
151 stdout = s.getvalue()
152 self.assertEqual(stdout, textwrap.dedent("""\
153 ---------- MESSAGE FOLLOWS ----------
154 b'From: test'
155 b'X-Peer: peer-address'
156 b''
157 b'h\\xc3\\xa9llo\\xff'
158 ------------ END MESSAGE ------------
159 """))
160
161 def test_process_SMTPUTF8_message_with_enable_SMTPUTF8_true(self):
162 server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0),
163 enable_SMTPUTF8=True)
164 conn, addr = server.accept()
165 channel = smtpd.SMTPChannel(server, conn, addr, enable_SMTPUTF8=True)
166 with support.captured_stdout() as s:
167 self.send_data(channel, b'From: test\n\nh\xc3\xa9llo\xff\n',
168 enable_SMTPUTF8=True)
169 stdout = s.getvalue()
170 self.assertEqual(stdout, textwrap.dedent("""\
171 ----- SMTPUTF8 MESSAGE FOLLOWS ------
172 b'From: test'
173 b'X-Peer: peer-address'
174 b''
175 b'h\\xc3\\xa9llo\\xff'
176 ------------ END MESSAGE ------------
177 """))
178
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000179 def tearDown(self):
Richard Jonesdaf23502010-08-16 01:48:14 +0000180 asyncore.close_all()
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000181 asyncore.socket = smtpd.socket = socket
182
183
R David Murray6fe56a32014-06-11 13:48:58 -0400184class TestFamilyDetection(unittest.TestCase):
185 def setUp(self):
186 smtpd.socket = asyncore.socket = mock_socket
187
188 def tearDown(self):
189 asyncore.close_all()
190 asyncore.socket = smtpd.socket = socket
191
192 @unittest.skipUnless(support.IPV6_ENABLED, "IPv6 not enabled")
193 def test_socket_uses_IPv6(self):
194 server = smtpd.SMTPServer((support.HOSTv6, 0), (support.HOST, 0),
195 decode_data=False)
196 self.assertEqual(server.socket.family, socket.AF_INET6)
197
198 def test_socket_uses_IPv4(self):
199 server = smtpd.SMTPServer((support.HOST, 0), (support.HOSTv6, 0),
200 decode_data=False)
201 self.assertEqual(server.socket.family, socket.AF_INET)
202
203
R David Murrayd1a30c92012-05-26 14:33:59 -0400204class SMTPDChannelTest(unittest.TestCase):
Richard Jones8cb36192010-07-23 16:20:40 +0000205 def setUp(self):
Richard Jones64b02de2010-08-03 06:39:33 +0000206 smtpd.socket = asyncore.socket = mock_socket
Antoine Pitroue0815e22011-11-12 20:36:29 +0100207 self.old_debugstream = smtpd.DEBUGSTREAM
Richard Jones8cb36192010-07-23 16:20:40 +0000208 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
R David Murray2539e672014-08-09 16:40:49 -0400209 self.server = DummyServer((support.HOST, 0), ('b', 0),
210 decode_data=True)
Richard Jones8cb36192010-07-23 16:20:40 +0000211 conn, addr = self.server.accept()
R David Murray554bcbf2014-06-11 11:18:08 -0400212 self.channel = smtpd.SMTPChannel(self.server, conn, addr,
213 decode_data=True)
Richard Jones8cb36192010-07-23 16:20:40 +0000214
Richard Jones64b02de2010-08-03 06:39:33 +0000215 def tearDown(self):
Richard Jonesdaf23502010-08-16 01:48:14 +0000216 asyncore.close_all()
Richard Jones64b02de2010-08-03 06:39:33 +0000217 asyncore.socket = smtpd.socket = socket
Antoine Pitroue0815e22011-11-12 20:36:29 +0100218 smtpd.DEBUGSTREAM = self.old_debugstream
Richard Jones64b02de2010-08-03 06:39:33 +0000219
Richard Jones8cb36192010-07-23 16:20:40 +0000220 def write_line(self, line):
221 self.channel.socket.queue_recv(line)
222 self.channel.handle_read()
223
224 def test_broken_connect(self):
R David Murray6fe56a32014-06-11 13:48:58 -0400225 self.assertRaises(
226 DummyDispatcherBroken, BrokenDummyServer,
R David Murray2539e672014-08-09 16:40:49 -0400227 (support.HOST, 0), ('b', 0), decode_data=True)
Richard Jones8cb36192010-07-23 16:20:40 +0000228
229 def test_server_accept(self):
230 self.server.handle_accept()
231
232 def test_missing_data(self):
233 self.write_line(b'')
Georg Brandl17e3d692010-07-31 10:08:09 +0000234 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000235 b'500 Error: bad syntax\r\n')
236
R David Murrayd1a30c92012-05-26 14:33:59 -0400237 def test_EHLO(self):
238 self.write_line(b'EHLO example')
239 self.assertEqual(self.channel.socket.last, b'250 HELP\r\n')
240
241 def test_EHLO_bad_syntax(self):
242 self.write_line(b'EHLO')
Georg Brandl17e3d692010-07-31 10:08:09 +0000243 self.assertEqual(self.channel.socket.last,
R David Murrayd1a30c92012-05-26 14:33:59 -0400244 b'501 Syntax: EHLO hostname\r\n')
245
246 def test_EHLO_duplicate(self):
247 self.write_line(b'EHLO example')
248 self.write_line(b'EHLO example')
249 self.assertEqual(self.channel.socket.last,
250 b'503 Duplicate HELO/EHLO\r\n')
251
252 def test_EHLO_HELO_duplicate(self):
253 self.write_line(b'EHLO example')
254 self.write_line(b'HELO example')
255 self.assertEqual(self.channel.socket.last,
256 b'503 Duplicate HELO/EHLO\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000257
258 def test_HELO(self):
Richard Jones64b02de2010-08-03 06:39:33 +0000259 name = smtpd.socket.getfqdn()
R David Murrayd1a30c92012-05-26 14:33:59 -0400260 self.write_line(b'HELO example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000261 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000262 '250 {}\r\n'.format(name).encode('ascii'))
263
R David Murrayd1a30c92012-05-26 14:33:59 -0400264 def test_HELO_EHLO_duplicate(self):
265 self.write_line(b'HELO example')
266 self.write_line(b'EHLO example')
267 self.assertEqual(self.channel.socket.last,
268 b'503 Duplicate HELO/EHLO\r\n')
269
270 def test_HELP(self):
271 self.write_line(b'HELP')
272 self.assertEqual(self.channel.socket.last,
273 b'250 Supported commands: EHLO HELO MAIL RCPT ' + \
274 b'DATA RSET NOOP QUIT VRFY\r\n')
275
276 def test_HELP_command(self):
277 self.write_line(b'HELP MAIL')
278 self.assertEqual(self.channel.socket.last,
279 b'250 Syntax: MAIL FROM: <address>\r\n')
280
281 def test_HELP_command_unknown(self):
282 self.write_line(b'HELP SPAM')
283 self.assertEqual(self.channel.socket.last,
284 b'501 Supported commands: EHLO HELO MAIL RCPT ' + \
285 b'DATA RSET NOOP QUIT VRFY\r\n')
286
Richard Jones8cb36192010-07-23 16:20:40 +0000287 def test_HELO_bad_syntax(self):
288 self.write_line(b'HELO')
Georg Brandl17e3d692010-07-31 10:08:09 +0000289 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000290 b'501 Syntax: HELO hostname\r\n')
291
292 def test_HELO_duplicate(self):
R David Murrayd1a30c92012-05-26 14:33:59 -0400293 self.write_line(b'HELO example')
294 self.write_line(b'HELO example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000295 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000296 b'503 Duplicate HELO/EHLO\r\n')
297
R David Murrayd1a30c92012-05-26 14:33:59 -0400298 def test_HELO_parameter_rejected_when_extensions_not_enabled(self):
299 self.extended_smtp = False
300 self.write_line(b'HELO example')
301 self.write_line(b'MAIL from:<foo@example.com> SIZE=1234')
302 self.assertEqual(self.channel.socket.last,
303 b'501 Syntax: MAIL FROM: <address>\r\n')
304
305 def test_MAIL_allows_space_after_colon(self):
306 self.write_line(b'HELO example')
307 self.write_line(b'MAIL from: <foo@example.com>')
308 self.assertEqual(self.channel.socket.last,
309 b'250 OK\r\n')
310
311 def test_extended_MAIL_allows_space_after_colon(self):
312 self.write_line(b'EHLO example')
313 self.write_line(b'MAIL from: <foo@example.com> size=20')
314 self.assertEqual(self.channel.socket.last,
315 b'250 OK\r\n')
316
Richard Jones8cb36192010-07-23 16:20:40 +0000317 def test_NOOP(self):
318 self.write_line(b'NOOP')
R David Murrayd1a30c92012-05-26 14:33:59 -0400319 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000320
R David Murray669b7552012-03-20 16:16:29 -0400321 def test_HELO_NOOP(self):
322 self.write_line(b'HELO example')
323 self.write_line(b'NOOP')
R David Murrayd1a30c92012-05-26 14:33:59 -0400324 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
R David Murray669b7552012-03-20 16:16:29 -0400325
Richard Jones8cb36192010-07-23 16:20:40 +0000326 def test_NOOP_bad_syntax(self):
327 self.write_line(b'NOOP hi')
Georg Brandl17e3d692010-07-31 10:08:09 +0000328 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000329 b'501 Syntax: NOOP\r\n')
330
331 def test_QUIT(self):
332 self.write_line(b'QUIT')
Georg Brandl17e3d692010-07-31 10:08:09 +0000333 self.assertEqual(self.channel.socket.last, b'221 Bye\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000334
R David Murray669b7552012-03-20 16:16:29 -0400335 def test_HELO_QUIT(self):
336 self.write_line(b'HELO example')
337 self.write_line(b'QUIT')
338 self.assertEqual(self.channel.socket.last, b'221 Bye\r\n')
339
Richard Jones8cb36192010-07-23 16:20:40 +0000340 def test_QUIT_arg_ignored(self):
341 self.write_line(b'QUIT bye bye')
Georg Brandl17e3d692010-07-31 10:08:09 +0000342 self.assertEqual(self.channel.socket.last, b'221 Bye\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000343
344 def test_bad_state(self):
Georg Brandl17e3d692010-07-31 10:08:09 +0000345 self.channel.smtp_state = 'BAD STATE'
R David Murray669b7552012-03-20 16:16:29 -0400346 self.write_line(b'HELO example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000347 self.assertEqual(self.channel.socket.last,
348 b'451 Internal confusion\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000349
Georg Brandl1e5c5f82010-12-03 07:38:22 +0000350 def test_command_too_long(self):
R David Murray669b7552012-03-20 16:16:29 -0400351 self.write_line(b'HELO example')
R David Murrayd1a30c92012-05-26 14:33:59 -0400352 self.write_line(b'MAIL from: ' +
Georg Brandl1e5c5f82010-12-03 07:38:22 +0000353 b'a' * self.channel.command_size_limit +
354 b'@example')
355 self.assertEqual(self.channel.socket.last,
356 b'500 Error: line too long\r\n')
357
R David Murrayd1a30c92012-05-26 14:33:59 -0400358 def test_MAIL_command_limit_extended_with_SIZE(self):
359 self.write_line(b'EHLO example')
360 fill_len = self.channel.command_size_limit - len('MAIL from:<@example>')
361 self.write_line(b'MAIL from:<' +
362 b'a' * fill_len +
363 b'@example> SIZE=1234')
364 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
365
366 self.write_line(b'MAIL from:<' +
367 b'a' * (fill_len + 26) +
368 b'@example> SIZE=1234')
369 self.assertEqual(self.channel.socket.last,
370 b'500 Error: line too long\r\n')
371
R David Murray2539e672014-08-09 16:40:49 -0400372 def test_MAIL_command_rejects_SMTPUTF8_by_default(self):
373 self.write_line(b'EHLO example')
374 self.write_line(
375 b'MAIL from: <naive@example.com> BODY=8BITMIME SMTPUTF8')
376 self.assertEqual(self.channel.socket.last[0:1], b'5')
377
R David Murrayd1a30c92012-05-26 14:33:59 -0400378 def test_data_longer_than_default_data_size_limit(self):
379 # Hack the default so we don't have to generate so much data.
380 self.channel.data_size_limit = 1048
R David Murray669b7552012-03-20 16:16:29 -0400381 self.write_line(b'HELO example')
Georg Brandl1e5c5f82010-12-03 07:38:22 +0000382 self.write_line(b'MAIL From:eggs@example')
383 self.write_line(b'RCPT To:spam@example')
384 self.write_line(b'DATA')
385 self.write_line(b'A' * self.channel.data_size_limit +
386 b'A\r\n.')
387 self.assertEqual(self.channel.socket.last,
388 b'552 Error: Too much mail data\r\n')
389
R David Murrayd1a30c92012-05-26 14:33:59 -0400390 def test_MAIL_size_parameter(self):
391 self.write_line(b'EHLO example')
392 self.write_line(b'MAIL FROM:<eggs@example> SIZE=512')
393 self.assertEqual(self.channel.socket.last,
394 b'250 OK\r\n')
395
396 def test_MAIL_invalid_size_parameter(self):
397 self.write_line(b'EHLO example')
398 self.write_line(b'MAIL FROM:<eggs@example> SIZE=invalid')
399 self.assertEqual(self.channel.socket.last,
400 b'501 Syntax: MAIL FROM: <address> [SP <mail-parameters>]\r\n')
401
402 def test_MAIL_RCPT_unknown_parameters(self):
403 self.write_line(b'EHLO example')
404 self.write_line(b'MAIL FROM:<eggs@example> ham=green')
405 self.assertEqual(self.channel.socket.last,
406 b'555 MAIL FROM parameters not recognized or not implemented\r\n')
407
408 self.write_line(b'MAIL FROM:<eggs@example>')
409 self.write_line(b'RCPT TO:<eggs@example> ham=green')
410 self.assertEqual(self.channel.socket.last,
411 b'555 RCPT TO parameters not recognized or not implemented\r\n')
412
413 def test_MAIL_size_parameter_larger_than_default_data_size_limit(self):
414 self.channel.data_size_limit = 1048
415 self.write_line(b'EHLO example')
416 self.write_line(b'MAIL FROM:<eggs@example> SIZE=2096')
417 self.assertEqual(self.channel.socket.last,
418 b'552 Error: message size exceeds fixed maximum message size\r\n')
419
Richard Jones8cb36192010-07-23 16:20:40 +0000420 def test_need_MAIL(self):
R David Murray669b7552012-03-20 16:16:29 -0400421 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000422 self.write_line(b'RCPT to:spam@example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000423 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000424 b'503 Error: need MAIL command\r\n')
425
R David Murrayd1a30c92012-05-26 14:33:59 -0400426 def test_MAIL_syntax_HELO(self):
R David Murray669b7552012-03-20 16:16:29 -0400427 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000428 self.write_line(b'MAIL from eggs@example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000429 self.assertEqual(self.channel.socket.last,
R David Murrayd1a30c92012-05-26 14:33:59 -0400430 b'501 Syntax: MAIL FROM: <address>\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000431
R David Murrayd1a30c92012-05-26 14:33:59 -0400432 def test_MAIL_syntax_EHLO(self):
433 self.write_line(b'EHLO example')
434 self.write_line(b'MAIL from eggs@example')
435 self.assertEqual(self.channel.socket.last,
436 b'501 Syntax: MAIL FROM: <address> [SP <mail-parameters>]\r\n')
437
438 def test_MAIL_missing_address(self):
R David Murray669b7552012-03-20 16:16:29 -0400439 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000440 self.write_line(b'MAIL from:')
Georg Brandl17e3d692010-07-31 10:08:09 +0000441 self.assertEqual(self.channel.socket.last,
R David Murrayd1a30c92012-05-26 14:33:59 -0400442 b'501 Syntax: MAIL FROM: <address>\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000443
444 def test_MAIL_chevrons(self):
R David Murray669b7552012-03-20 16:16:29 -0400445 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000446 self.write_line(b'MAIL from:<eggs@example>')
R David Murrayd1a30c92012-05-26 14:33:59 -0400447 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
448
449 def test_MAIL_empty_chevrons(self):
450 self.write_line(b'EHLO example')
451 self.write_line(b'MAIL from:<>')
452 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
453
454 def test_MAIL_quoted_localpart(self):
455 self.write_line(b'EHLO example')
456 self.write_line(b'MAIL from: <"Fred Blogs"@example.com>')
457 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
458 self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
459
460 def test_MAIL_quoted_localpart_no_angles(self):
461 self.write_line(b'EHLO example')
462 self.write_line(b'MAIL from: "Fred Blogs"@example.com')
463 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
464 self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
465
466 def test_MAIL_quoted_localpart_with_size(self):
467 self.write_line(b'EHLO example')
468 self.write_line(b'MAIL from: <"Fred Blogs"@example.com> SIZE=1000')
469 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
470 self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
471
472 def test_MAIL_quoted_localpart_with_size_no_angles(self):
473 self.write_line(b'EHLO example')
474 self.write_line(b'MAIL from: "Fred Blogs"@example.com SIZE=1000')
475 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
476 self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
Richard Jones8cb36192010-07-23 16:20:40 +0000477
478 def test_nested_MAIL(self):
R David Murray669b7552012-03-20 16:16:29 -0400479 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000480 self.write_line(b'MAIL from:eggs@example')
481 self.write_line(b'MAIL from:spam@example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000482 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000483 b'503 Error: nested MAIL command\r\n')
484
R David Murrayd1a30c92012-05-26 14:33:59 -0400485 def test_VRFY(self):
486 self.write_line(b'VRFY eggs@example')
487 self.assertEqual(self.channel.socket.last,
488 b'252 Cannot VRFY user, but will accept message and attempt ' + \
489 b'delivery\r\n')
490
491 def test_VRFY_syntax(self):
492 self.write_line(b'VRFY')
493 self.assertEqual(self.channel.socket.last,
494 b'501 Syntax: VRFY <address>\r\n')
495
496 def test_EXPN_not_implemented(self):
497 self.write_line(b'EXPN')
498 self.assertEqual(self.channel.socket.last,
499 b'502 EXPN not implemented\r\n')
500
R David Murray669b7552012-03-20 16:16:29 -0400501 def test_no_HELO_MAIL(self):
502 self.write_line(b'MAIL from:<foo@example.com>')
503 self.assertEqual(self.channel.socket.last,
504 b'503 Error: send HELO first\r\n')
505
Richard Jones8cb36192010-07-23 16:20:40 +0000506 def test_need_RCPT(self):
R David Murray669b7552012-03-20 16:16:29 -0400507 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000508 self.write_line(b'MAIL From:eggs@example')
509 self.write_line(b'DATA')
Georg Brandl17e3d692010-07-31 10:08:09 +0000510 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000511 b'503 Error: need RCPT command\r\n')
512
R David Murrayd1a30c92012-05-26 14:33:59 -0400513 def test_RCPT_syntax_HELO(self):
R David Murray669b7552012-03-20 16:16:29 -0400514 self.write_line(b'HELO example')
R David Murrayd1a30c92012-05-26 14:33:59 -0400515 self.write_line(b'MAIL From: eggs@example')
Richard Jones8cb36192010-07-23 16:20:40 +0000516 self.write_line(b'RCPT to eggs@example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000517 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000518 b'501 Syntax: RCPT TO: <address>\r\n')
519
R David Murrayd1a30c92012-05-26 14:33:59 -0400520 def test_RCPT_syntax_EHLO(self):
521 self.write_line(b'EHLO example')
522 self.write_line(b'MAIL From: eggs@example')
523 self.write_line(b'RCPT to eggs@example')
524 self.assertEqual(self.channel.socket.last,
525 b'501 Syntax: RCPT TO: <address> [SP <mail-parameters>]\r\n')
526
527 def test_RCPT_lowercase_to_OK(self):
528 self.write_line(b'HELO example')
529 self.write_line(b'MAIL From: eggs@example')
530 self.write_line(b'RCPT to: <eggs@example>')
531 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
532
R David Murray669b7552012-03-20 16:16:29 -0400533 def test_no_HELO_RCPT(self):
534 self.write_line(b'RCPT to eggs@example')
535 self.assertEqual(self.channel.socket.last,
536 b'503 Error: send HELO first\r\n')
537
Richard Jones8cb36192010-07-23 16:20:40 +0000538 def test_data_dialog(self):
R David Murray669b7552012-03-20 16:16:29 -0400539 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000540 self.write_line(b'MAIL From:eggs@example')
R David Murrayd1a30c92012-05-26 14:33:59 -0400541 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000542 self.write_line(b'RCPT To:spam@example')
R David Murrayd1a30c92012-05-26 14:33:59 -0400543 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000544
545 self.write_line(b'DATA')
Georg Brandl17e3d692010-07-31 10:08:09 +0000546 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000547 b'354 End data with <CR><LF>.<CR><LF>\r\n')
548 self.write_line(b'data\r\nmore\r\n.')
R David Murrayd1a30c92012-05-26 14:33:59 -0400549 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000550 self.assertEqual(self.server.messages,
R David Murray2539e672014-08-09 16:40:49 -0400551 [(('peer-address', 'peer-port'),
552 'eggs@example',
553 ['spam@example'],
554 'data\nmore')])
Richard Jones8cb36192010-07-23 16:20:40 +0000555
556 def test_DATA_syntax(self):
R David Murray669b7552012-03-20 16:16:29 -0400557 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000558 self.write_line(b'MAIL From:eggs@example')
559 self.write_line(b'RCPT To:spam@example')
560 self.write_line(b'DATA spam')
Georg Brandl17e3d692010-07-31 10:08:09 +0000561 self.assertEqual(self.channel.socket.last, b'501 Syntax: DATA\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000562
R David Murray669b7552012-03-20 16:16:29 -0400563 def test_no_HELO_DATA(self):
564 self.write_line(b'DATA spam')
565 self.assertEqual(self.channel.socket.last,
566 b'503 Error: send HELO first\r\n')
567
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000568 def test_data_transparency_section_4_5_2(self):
R David Murray669b7552012-03-20 16:16:29 -0400569 self.write_line(b'HELO example')
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000570 self.write_line(b'MAIL From:eggs@example')
571 self.write_line(b'RCPT To:spam@example')
572 self.write_line(b'DATA')
573 self.write_line(b'..\r\n.\r\n')
574 self.assertEqual(self.channel.received_data, '.')
575
Richard Jones8cb36192010-07-23 16:20:40 +0000576 def test_multiple_RCPT(self):
R David Murray669b7552012-03-20 16:16:29 -0400577 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000578 self.write_line(b'MAIL From:eggs@example')
579 self.write_line(b'RCPT To:spam@example')
580 self.write_line(b'RCPT To:ham@example')
581 self.write_line(b'DATA')
582 self.write_line(b'data\r\n.')
Richard Jones6a9e6bb2010-08-04 12:27:36 +0000583 self.assertEqual(self.server.messages,
R David Murray2539e672014-08-09 16:40:49 -0400584 [(('peer-address', 'peer-port'),
585 'eggs@example',
586 ['spam@example','ham@example'],
587 'data')])
Richard Jones8cb36192010-07-23 16:20:40 +0000588
589 def test_manual_status(self):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000590 # checks that the Channel is able to return a custom status message
R David Murray669b7552012-03-20 16:16:29 -0400591 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000592 self.write_line(b'MAIL From:eggs@example')
593 self.write_line(b'RCPT To:spam@example')
594 self.write_line(b'DATA')
595 self.write_line(b'return status\r\n.')
Georg Brandl17e3d692010-07-31 10:08:09 +0000596 self.assertEqual(self.channel.socket.last, b'250 Okish\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000597
598 def test_RSET(self):
R David Murray669b7552012-03-20 16:16:29 -0400599 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000600 self.write_line(b'MAIL From:eggs@example')
601 self.write_line(b'RCPT To:spam@example')
602 self.write_line(b'RSET')
R David Murrayd1a30c92012-05-26 14:33:59 -0400603 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000604 self.write_line(b'MAIL From:foo@example')
605 self.write_line(b'RCPT To:eggs@example')
606 self.write_line(b'DATA')
607 self.write_line(b'data\r\n.')
Richard Jones6a9e6bb2010-08-04 12:27:36 +0000608 self.assertEqual(self.server.messages,
R David Murray2539e672014-08-09 16:40:49 -0400609 [(('peer-address', 'peer-port'),
610 'foo@example',
611 ['eggs@example'],
612 'data')])
Richard Jones8cb36192010-07-23 16:20:40 +0000613
R David Murray669b7552012-03-20 16:16:29 -0400614 def test_HELO_RSET(self):
615 self.write_line(b'HELO example')
616 self.write_line(b'RSET')
R David Murrayd1a30c92012-05-26 14:33:59 -0400617 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
R David Murray669b7552012-03-20 16:16:29 -0400618
Richard Jones8cb36192010-07-23 16:20:40 +0000619 def test_RSET_syntax(self):
620 self.write_line(b'RSET hi')
Georg Brandl17e3d692010-07-31 10:08:09 +0000621 self.assertEqual(self.channel.socket.last, b'501 Syntax: RSET\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000622
R David Murrayd1a30c92012-05-26 14:33:59 -0400623 def test_unknown_command(self):
624 self.write_line(b'UNKNOWN_CMD')
625 self.assertEqual(self.channel.socket.last,
626 b'500 Error: command "UNKNOWN_CMD" not ' + \
627 b'recognized\r\n')
628
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000629 def test_attribute_deprecations(self):
Florent Xicluna67317752011-12-10 11:07:42 +0100630 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000631 spam = self.channel._SMTPChannel__server
Florent Xicluna67317752011-12-10 11:07:42 +0100632 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000633 self.channel._SMTPChannel__server = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100634 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000635 spam = self.channel._SMTPChannel__line
Florent Xicluna67317752011-12-10 11:07:42 +0100636 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000637 self.channel._SMTPChannel__line = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100638 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000639 spam = self.channel._SMTPChannel__state
Florent Xicluna67317752011-12-10 11:07:42 +0100640 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000641 self.channel._SMTPChannel__state = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100642 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000643 spam = self.channel._SMTPChannel__greeting
Florent Xicluna67317752011-12-10 11:07:42 +0100644 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000645 self.channel._SMTPChannel__greeting = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100646 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000647 spam = self.channel._SMTPChannel__mailfrom
Florent Xicluna67317752011-12-10 11:07:42 +0100648 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000649 self.channel._SMTPChannel__mailfrom = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100650 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000651 spam = self.channel._SMTPChannel__rcpttos
Florent Xicluna67317752011-12-10 11:07:42 +0100652 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000653 self.channel._SMTPChannel__rcpttos = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100654 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000655 spam = self.channel._SMTPChannel__data
Florent Xicluna67317752011-12-10 11:07:42 +0100656 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000657 self.channel._SMTPChannel__data = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100658 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000659 spam = self.channel._SMTPChannel__fqdn
Florent Xicluna67317752011-12-10 11:07:42 +0100660 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000661 self.channel._SMTPChannel__fqdn = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100662 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000663 spam = self.channel._SMTPChannel__peer
Florent Xicluna67317752011-12-10 11:07:42 +0100664 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000665 self.channel._SMTPChannel__peer = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100666 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000667 spam = self.channel._SMTPChannel__conn
Florent Xicluna67317752011-12-10 11:07:42 +0100668 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000669 self.channel._SMTPChannel__conn = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100670 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000671 spam = self.channel._SMTPChannel__addr
Florent Xicluna67317752011-12-10 11:07:42 +0100672 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000673 self.channel._SMTPChannel__addr = 'spam'
Richard Jones8cb36192010-07-23 16:20:40 +0000674
R David Murray554bcbf2014-06-11 11:18:08 -0400675 def test_decode_data_default_warning(self):
R David Murray2539e672014-08-09 16:40:49 -0400676 with self.assertWarns(DeprecationWarning):
677 server = DummyServer((support.HOST, 0), ('b', 0))
R David Murray554bcbf2014-06-11 11:18:08 -0400678 conn, addr = self.server.accept()
679 with self.assertWarns(DeprecationWarning):
680 smtpd.SMTPChannel(server, conn, addr)
681
R David Murray6fe56a32014-06-11 13:48:58 -0400682@unittest.skipUnless(support.IPV6_ENABLED, "IPv6 not enabled")
683class SMTPDChannelIPv6Test(SMTPDChannelTest):
684 def setUp(self):
685 smtpd.socket = asyncore.socket = mock_socket
686 self.old_debugstream = smtpd.DEBUGSTREAM
687 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
R David Murray2539e672014-08-09 16:40:49 -0400688 self.server = DummyServer((support.HOSTv6, 0), ('b', 0),
689 decode_data=True)
R David Murray6fe56a32014-06-11 13:48:58 -0400690 conn, addr = self.server.accept()
691 self.channel = smtpd.SMTPChannel(self.server, conn, addr,
692 decode_data=True)
R David Murrayd1a30c92012-05-26 14:33:59 -0400693
694class SMTPDChannelWithDataSizeLimitTest(unittest.TestCase):
695
696 def setUp(self):
697 smtpd.socket = asyncore.socket = mock_socket
R David Murray05cab752012-06-04 15:55:51 -0400698 self.old_debugstream = smtpd.DEBUGSTREAM
R David Murrayd1a30c92012-05-26 14:33:59 -0400699 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
R David Murray2539e672014-08-09 16:40:49 -0400700 self.server = DummyServer((support.HOST, 0), ('b', 0),
701 decode_data=True)
R David Murrayd1a30c92012-05-26 14:33:59 -0400702 conn, addr = self.server.accept()
703 # Set DATA size limit to 32 bytes for easy testing
R David Murray554bcbf2014-06-11 11:18:08 -0400704 self.channel = smtpd.SMTPChannel(self.server, conn, addr, 32,
705 decode_data=True)
R David Murrayd1a30c92012-05-26 14:33:59 -0400706
707 def tearDown(self):
708 asyncore.close_all()
709 asyncore.socket = smtpd.socket = socket
R David Murray05cab752012-06-04 15:55:51 -0400710 smtpd.DEBUGSTREAM = self.old_debugstream
R David Murrayd1a30c92012-05-26 14:33:59 -0400711
712 def write_line(self, line):
713 self.channel.socket.queue_recv(line)
714 self.channel.handle_read()
715
716 def test_data_limit_dialog(self):
717 self.write_line(b'HELO example')
718 self.write_line(b'MAIL From:eggs@example')
719 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
720 self.write_line(b'RCPT To:spam@example')
721 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
722
723 self.write_line(b'DATA')
724 self.assertEqual(self.channel.socket.last,
725 b'354 End data with <CR><LF>.<CR><LF>\r\n')
726 self.write_line(b'data\r\nmore\r\n.')
727 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
728 self.assertEqual(self.server.messages,
R David Murray2539e672014-08-09 16:40:49 -0400729 [(('peer-address', 'peer-port'),
730 'eggs@example',
731 ['spam@example'],
732 'data\nmore')])
R David Murrayd1a30c92012-05-26 14:33:59 -0400733
734 def test_data_limit_dialog_too_much_data(self):
735 self.write_line(b'HELO example')
736 self.write_line(b'MAIL From:eggs@example')
737 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
738 self.write_line(b'RCPT To:spam@example')
739 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
740
741 self.write_line(b'DATA')
742 self.assertEqual(self.channel.socket.last,
743 b'354 End data with <CR><LF>.<CR><LF>\r\n')
744 self.write_line(b'This message is longer than 32 bytes\r\n.')
745 self.assertEqual(self.channel.socket.last,
746 b'552 Error: Too much mail data\r\n')
747
Richard Jones8cb36192010-07-23 16:20:40 +0000748
R David Murray554bcbf2014-06-11 11:18:08 -0400749class SMTPDChannelWithDecodeDataFalse(unittest.TestCase):
750
751 def setUp(self):
752 smtpd.socket = asyncore.socket = mock_socket
753 self.old_debugstream = smtpd.DEBUGSTREAM
754 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
R David Murray6fe56a32014-06-11 13:48:58 -0400755 self.server = DummyServer((support.HOST, 0), ('b', 0),
756 decode_data=False)
R David Murray554bcbf2014-06-11 11:18:08 -0400757 conn, addr = self.server.accept()
758 # Set decode_data to False
759 self.channel = smtpd.SMTPChannel(self.server, conn, addr,
760 decode_data=False)
761
762 def tearDown(self):
763 asyncore.close_all()
764 asyncore.socket = smtpd.socket = socket
765 smtpd.DEBUGSTREAM = self.old_debugstream
766
767 def write_line(self, line):
768 self.channel.socket.queue_recv(line)
769 self.channel.handle_read()
770
771 def test_ascii_data(self):
772 self.write_line(b'HELO example')
773 self.write_line(b'MAIL From:eggs@example')
774 self.write_line(b'RCPT To:spam@example')
775 self.write_line(b'DATA')
776 self.write_line(b'plain ascii text')
777 self.write_line(b'.')
778 self.assertEqual(self.channel.received_data, b'plain ascii text')
779
780 def test_utf8_data(self):
781 self.write_line(b'HELO example')
782 self.write_line(b'MAIL From:eggs@example')
783 self.write_line(b'RCPT To:spam@example')
784 self.write_line(b'DATA')
785 self.write_line(b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87')
786 self.write_line(b'and some plain ascii')
787 self.write_line(b'.')
788 self.assertEqual(
789 self.channel.received_data,
790 b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87\n'
791 b'and some plain ascii')
792
793
794class SMTPDChannelWithDecodeDataTrue(unittest.TestCase):
795
796 def setUp(self):
797 smtpd.socket = asyncore.socket = mock_socket
798 self.old_debugstream = smtpd.DEBUGSTREAM
799 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
R David Murray6fe56a32014-06-11 13:48:58 -0400800 self.server = DummyServer((support.HOST, 0), ('b', 0),
801 decode_data=True)
R David Murray554bcbf2014-06-11 11:18:08 -0400802 conn, addr = self.server.accept()
803 # Set decode_data to True
804 self.channel = smtpd.SMTPChannel(self.server, conn, addr,
805 decode_data=True)
806
807 def tearDown(self):
808 asyncore.close_all()
809 asyncore.socket = smtpd.socket = socket
810 smtpd.DEBUGSTREAM = self.old_debugstream
811
812 def write_line(self, line):
813 self.channel.socket.queue_recv(line)
814 self.channel.handle_read()
815
816 def test_ascii_data(self):
817 self.write_line(b'HELO example')
818 self.write_line(b'MAIL From:eggs@example')
819 self.write_line(b'RCPT To:spam@example')
820 self.write_line(b'DATA')
821 self.write_line(b'plain ascii text')
822 self.write_line(b'.')
823 self.assertEqual(self.channel.received_data, 'plain ascii text')
824
825 def test_utf8_data(self):
826 self.write_line(b'HELO example')
827 self.write_line(b'MAIL From:eggs@example')
828 self.write_line(b'RCPT To:spam@example')
829 self.write_line(b'DATA')
830 self.write_line(b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87')
831 self.write_line(b'and some plain ascii')
832 self.write_line(b'.')
833 self.assertEqual(
834 self.channel.received_data,
835 'utf8 enriched text: żźć\nand some plain ascii')
836
837
R David Murray2539e672014-08-09 16:40:49 -0400838class SMTPDChannelTestWithEnableSMTPUTF8True(unittest.TestCase):
839 def setUp(self):
840 smtpd.socket = asyncore.socket = mock_socket
841 self.old_debugstream = smtpd.DEBUGSTREAM
842 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
843 self.server = DummyServer((support.HOST, 0), ('b', 0),
844 enable_SMTPUTF8=True)
845 conn, addr = self.server.accept()
846 self.channel = smtpd.SMTPChannel(self.server, conn, addr,
847 enable_SMTPUTF8=True)
848
849 def tearDown(self):
850 asyncore.close_all()
851 asyncore.socket = smtpd.socket = socket
852 smtpd.DEBUGSTREAM = self.old_debugstream
853
854 def write_line(self, line):
855 self.channel.socket.queue_recv(line)
856 self.channel.handle_read()
857
858 def test_MAIL_command_accepts_SMTPUTF8_when_announced(self):
859 self.write_line(b'EHLO example')
860 self.write_line(
861 'MAIL from: <naïve@example.com> BODY=8BITMIME SMTPUTF8'.encode(
862 'utf-8')
863 )
864 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
865
866 def test_process_smtputf8_message(self):
867 self.write_line(b'EHLO example')
868 for mail_parameters in [b'', b'BODY=8BITMIME SMTPUTF8']:
869 self.write_line(b'MAIL from: <a@example> ' + mail_parameters)
870 self.assertEqual(self.channel.socket.last[0:3], b'250')
871 self.write_line(b'rcpt to:<b@example.com>')
872 self.assertEqual(self.channel.socket.last[0:3], b'250')
873 self.write_line(b'data')
874 self.assertEqual(self.channel.socket.last[0:3], b'354')
875 self.write_line(b'c\r\n.')
876 if mail_parameters == b'':
877 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
878 else:
879 self.assertEqual(self.channel.socket.last,
880 b'250 SMTPUTF8 message okish\r\n')
881
882 def test_utf8_data(self):
883 self.write_line(b'EHLO example')
884 self.write_line(
885 'MAIL From: naïve@examplé BODY=8BITMIME SMTPUTF8'.encode('utf-8'))
886 self.assertEqual(self.channel.socket.last[0:3], b'250')
887 self.write_line('RCPT To:späm@examplé'.encode('utf-8'))
888 self.assertEqual(self.channel.socket.last[0:3], b'250')
889 self.write_line(b'DATA')
890 self.assertEqual(self.channel.socket.last[0:3], b'354')
891 self.write_line(b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87')
892 self.write_line(b'.')
893 self.assertEqual(
894 self.channel.received_data,
895 b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87')
896
897 def test_MAIL_command_limit_extended_with_SIZE_and_SMTPUTF8(self):
898 self.write_line(b'ehlo example')
899 fill_len = (512 + 26 + 10) - len('mail from:<@example>')
900 self.write_line(b'MAIL from:<' +
901 b'a' * (fill_len + 1) +
902 b'@example>')
903 self.assertEqual(self.channel.socket.last,
904 b'500 Error: line too long\r\n')
905 self.write_line(b'MAIL from:<' +
906 b'a' * fill_len +
907 b'@example>')
908 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
909
910 def test_multiple_emails_with_extended_command_length(self):
911 self.write_line(b'ehlo example')
912 fill_len = (512 + 26 + 10) - len('mail from:<@example>')
913 for char in [b'a', b'b', b'c']:
914 self.write_line(b'MAIL from:<' + char * fill_len + b'a@example>')
915 self.assertEqual(self.channel.socket.last[0:3], b'500')
916 self.write_line(b'MAIL from:<' + char * fill_len + b'@example>')
917 self.assertEqual(self.channel.socket.last[0:3], b'250')
918 self.write_line(b'rcpt to:<hans@example.com>')
919 self.assertEqual(self.channel.socket.last[0:3], b'250')
920 self.write_line(b'data')
921 self.assertEqual(self.channel.socket.last[0:3], b'354')
922 self.write_line(b'test\r\n.')
923 self.assertEqual(self.channel.socket.last[0:3], b'250')
924
Richard Jones8cb36192010-07-23 16:20:40 +0000925if __name__ == "__main__":
R David Murrayd1a30c92012-05-26 14:33:59 -0400926 unittest.main()