blob: a9f7d5a3b8bc19aec90d94cc51d3a441aafc7db9 [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
R David Murraya33df312015-05-11 12:11:40 -040019 def process_message(self, peer, mailfrom, rcpttos, data, **kw):
Richard Jones8cb36192010-07-23 16:20:40 +000020 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'
R David Murraya33df312015-05-11 12:11:40 -040023 if 'mail_options' in kw and 'SMTPUTF8' in kw['mail_options']:
24 return '250 SMTPUTF8 message okish'
R David Murray2539e672014-08-09 16:40:49 -040025
Richard Jones4aa0d4d2010-08-04 01:20:14 +000026
Richard Jones8cb36192010-07-23 16:20:40 +000027class DummyDispatcherBroken(Exception):
28 pass
29
Richard Jones4aa0d4d2010-08-04 01:20:14 +000030
Richard Jones8cb36192010-07-23 16:20:40 +000031class BrokenDummyServer(DummyServer):
32 def listen(self, num):
33 raise DummyDispatcherBroken()
34
Richard Jones4aa0d4d2010-08-04 01:20:14 +000035
R David Murrayd1a30c92012-05-26 14:33:59 -040036class SMTPDServerTest(unittest.TestCase):
Richard Jones4aa0d4d2010-08-04 01:20:14 +000037 def setUp(self):
38 smtpd.socket = asyncore.socket = mock_socket
39
40 def test_process_message_unimplemented(self):
R David Murray6fe56a32014-06-11 13:48:58 -040041 server = smtpd.SMTPServer((support.HOST, 0), ('b', 0),
42 decode_data=True)
Richard Jones4aa0d4d2010-08-04 01:20:14 +000043 conn, addr = server.accept()
R David Murray554bcbf2014-06-11 11:18:08 -040044 channel = smtpd.SMTPChannel(server, conn, addr, decode_data=True)
Richard Jones4aa0d4d2010-08-04 01:20:14 +000045
46 def write_line(line):
47 channel.socket.queue_recv(line)
48 channel.handle_read()
49
R David Murrayd1a30c92012-05-26 14:33:59 -040050 write_line(b'HELO example')
Richard Jones4aa0d4d2010-08-04 01:20:14 +000051 write_line(b'MAIL From:eggs@example')
52 write_line(b'RCPT To:spam@example')
53 write_line(b'DATA')
54 self.assertRaises(NotImplementedError, write_line, b'spam\r\n.\r\n')
55
R David Murray2539e672014-08-09 16:40:49 -040056 def test_decode_data_and_enable_SMTPUTF8_raises(self):
57 self.assertRaises(
58 ValueError,
59 smtpd.SMTPServer,
60 (support.HOST, 0),
61 ('b', 0),
62 enable_SMTPUTF8=True,
63 decode_data=True)
64
65 def tearDown(self):
66 asyncore.close_all()
67 asyncore.socket = smtpd.socket = socket
68
69
70class DebuggingServerTest(unittest.TestCase):
71
72 def setUp(self):
73 smtpd.socket = asyncore.socket = mock_socket
74
75 def send_data(self, channel, data, enable_SMTPUTF8=False):
76 def write_line(line):
77 channel.socket.queue_recv(line)
78 channel.handle_read()
79 write_line(b'EHLO example')
80 if enable_SMTPUTF8:
81 write_line(b'MAIL From:eggs@example BODY=8BITMIME SMTPUTF8')
82 else:
83 write_line(b'MAIL From:eggs@example')
84 write_line(b'RCPT To:spam@example')
85 write_line(b'DATA')
86 write_line(data)
87 write_line(b'.')
88
89 def test_process_message_with_decode_data_true(self):
90 server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0),
91 decode_data=True)
92 conn, addr = server.accept()
93 channel = smtpd.SMTPChannel(server, conn, addr, decode_data=True)
94 with support.captured_stdout() as s:
95 self.send_data(channel, b'From: test\n\nhello\n')
96 stdout = s.getvalue()
97 self.assertEqual(stdout, textwrap.dedent("""\
98 ---------- MESSAGE FOLLOWS ----------
99 From: test
100 X-Peer: peer-address
101
102 hello
103 ------------ END MESSAGE ------------
104 """))
105
106 def test_process_message_with_decode_data_false(self):
Serhiy Storchakacbcc2fd2016-05-16 09:36:31 +0300107 server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0))
R David Murray2539e672014-08-09 16:40:49 -0400108 conn, addr = server.accept()
Serhiy Storchakacbcc2fd2016-05-16 09:36:31 +0300109 channel = smtpd.SMTPChannel(server, conn, addr)
R David Murray2539e672014-08-09 16:40:49 -0400110 with support.captured_stdout() as s:
111 self.send_data(channel, b'From: test\n\nh\xc3\xa9llo\xff\n')
112 stdout = s.getvalue()
113 self.assertEqual(stdout, textwrap.dedent("""\
114 ---------- MESSAGE FOLLOWS ----------
115 b'From: test'
116 b'X-Peer: peer-address'
117 b''
118 b'h\\xc3\\xa9llo\\xff'
119 ------------ END MESSAGE ------------
120 """))
121
122 def test_process_message_with_enable_SMTPUTF8_true(self):
123 server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0),
124 enable_SMTPUTF8=True)
125 conn, addr = server.accept()
126 channel = smtpd.SMTPChannel(server, conn, addr, enable_SMTPUTF8=True)
127 with support.captured_stdout() as s:
128 self.send_data(channel, b'From: test\n\nh\xc3\xa9llo\xff\n')
129 stdout = s.getvalue()
130 self.assertEqual(stdout, textwrap.dedent("""\
131 ---------- MESSAGE FOLLOWS ----------
132 b'From: test'
133 b'X-Peer: peer-address'
134 b''
135 b'h\\xc3\\xa9llo\\xff'
136 ------------ END MESSAGE ------------
137 """))
138
139 def test_process_SMTPUTF8_message_with_enable_SMTPUTF8_true(self):
140 server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0),
141 enable_SMTPUTF8=True)
142 conn, addr = server.accept()
143 channel = smtpd.SMTPChannel(server, conn, addr, enable_SMTPUTF8=True)
144 with support.captured_stdout() as s:
145 self.send_data(channel, b'From: test\n\nh\xc3\xa9llo\xff\n',
146 enable_SMTPUTF8=True)
147 stdout = s.getvalue()
148 self.assertEqual(stdout, textwrap.dedent("""\
R David Murraya33df312015-05-11 12:11:40 -0400149 ---------- MESSAGE FOLLOWS ----------
150 mail options: ['BODY=8BITMIME', 'SMTPUTF8']
R David Murray2539e672014-08-09 16:40:49 -0400151 b'From: test'
152 b'X-Peer: peer-address'
153 b''
154 b'h\\xc3\\xa9llo\\xff'
155 ------------ END MESSAGE ------------
156 """))
157
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000158 def tearDown(self):
Richard Jonesdaf23502010-08-16 01:48:14 +0000159 asyncore.close_all()
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000160 asyncore.socket = smtpd.socket = socket
161
162
R David Murray6fe56a32014-06-11 13:48:58 -0400163class TestFamilyDetection(unittest.TestCase):
164 def setUp(self):
165 smtpd.socket = asyncore.socket = mock_socket
166
167 def tearDown(self):
168 asyncore.close_all()
169 asyncore.socket = smtpd.socket = socket
170
171 @unittest.skipUnless(support.IPV6_ENABLED, "IPv6 not enabled")
172 def test_socket_uses_IPv6(self):
Gregory P. Smithefb1d0a2017-09-09 00:30:15 -0700173 server = smtpd.SMTPServer((support.HOSTv6, 0), (support.HOSTv4, 0))
R David Murray6fe56a32014-06-11 13:48:58 -0400174 self.assertEqual(server.socket.family, socket.AF_INET6)
175
176 def test_socket_uses_IPv4(self):
Gregory P. Smithefb1d0a2017-09-09 00:30:15 -0700177 server = smtpd.SMTPServer((support.HOSTv4, 0), (support.HOSTv6, 0))
R David Murray6fe56a32014-06-11 13:48:58 -0400178 self.assertEqual(server.socket.family, socket.AF_INET)
179
180
R David Murraya33df312015-05-11 12:11:40 -0400181class TestRcptOptionParsing(unittest.TestCase):
182 error_response = (b'555 RCPT TO parameters not recognized or not '
183 b'implemented\r\n')
184
185 def setUp(self):
186 smtpd.socket = asyncore.socket = mock_socket
187 self.old_debugstream = smtpd.DEBUGSTREAM
188 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
189
190 def tearDown(self):
191 asyncore.close_all()
192 asyncore.socket = smtpd.socket = socket
193 smtpd.DEBUGSTREAM = self.old_debugstream
194
195 def write_line(self, channel, line):
196 channel.socket.queue_recv(line)
197 channel.handle_read()
198
199 def test_params_rejected(self):
Serhiy Storchakacbcc2fd2016-05-16 09:36:31 +0300200 server = DummyServer((support.HOST, 0), ('b', 0))
R David Murraya33df312015-05-11 12:11:40 -0400201 conn, addr = server.accept()
Serhiy Storchakacbcc2fd2016-05-16 09:36:31 +0300202 channel = smtpd.SMTPChannel(server, conn, addr)
R David Murraya33df312015-05-11 12:11:40 -0400203 self.write_line(channel, b'EHLO example')
204 self.write_line(channel, b'MAIL from: <foo@example.com> size=20')
205 self.write_line(channel, b'RCPT to: <foo@example.com> foo=bar')
206 self.assertEqual(channel.socket.last, self.error_response)
207
208 def test_nothing_accepted(self):
Serhiy Storchakacbcc2fd2016-05-16 09:36:31 +0300209 server = DummyServer((support.HOST, 0), ('b', 0))
R David Murraya33df312015-05-11 12:11:40 -0400210 conn, addr = server.accept()
Serhiy Storchakacbcc2fd2016-05-16 09:36:31 +0300211 channel = smtpd.SMTPChannel(server, conn, addr)
R David Murraya33df312015-05-11 12:11:40 -0400212 self.write_line(channel, b'EHLO example')
213 self.write_line(channel, b'MAIL from: <foo@example.com> size=20')
214 self.write_line(channel, b'RCPT to: <foo@example.com>')
215 self.assertEqual(channel.socket.last, b'250 OK\r\n')
216
217
218class TestMailOptionParsing(unittest.TestCase):
219 error_response = (b'555 MAIL FROM parameters not recognized or not '
220 b'implemented\r\n')
221
222 def setUp(self):
223 smtpd.socket = asyncore.socket = mock_socket
224 self.old_debugstream = smtpd.DEBUGSTREAM
225 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
226
227 def tearDown(self):
228 asyncore.close_all()
229 asyncore.socket = smtpd.socket = socket
230 smtpd.DEBUGSTREAM = self.old_debugstream
231
232 def write_line(self, channel, line):
233 channel.socket.queue_recv(line)
234 channel.handle_read()
235
236 def test_with_decode_data_true(self):
237 server = DummyServer((support.HOST, 0), ('b', 0), decode_data=True)
238 conn, addr = server.accept()
239 channel = smtpd.SMTPChannel(server, conn, addr, decode_data=True)
240 self.write_line(channel, b'EHLO example')
241 for line in [
242 b'MAIL from: <foo@example.com> size=20 SMTPUTF8',
243 b'MAIL from: <foo@example.com> size=20 SMTPUTF8 BODY=8BITMIME',
244 b'MAIL from: <foo@example.com> size=20 BODY=UNKNOWN',
245 b'MAIL from: <foo@example.com> size=20 body=8bitmime',
246 ]:
247 self.write_line(channel, line)
248 self.assertEqual(channel.socket.last, self.error_response)
249 self.write_line(channel, b'MAIL from: <foo@example.com> size=20')
250 self.assertEqual(channel.socket.last, b'250 OK\r\n')
251
252 def test_with_decode_data_false(self):
Serhiy Storchakacbcc2fd2016-05-16 09:36:31 +0300253 server = DummyServer((support.HOST, 0), ('b', 0))
R David Murraya33df312015-05-11 12:11:40 -0400254 conn, addr = server.accept()
Serhiy Storchakacbcc2fd2016-05-16 09:36:31 +0300255 channel = smtpd.SMTPChannel(server, conn, addr)
R David Murraya33df312015-05-11 12:11:40 -0400256 self.write_line(channel, b'EHLO example')
257 for line in [
258 b'MAIL from: <foo@example.com> size=20 SMTPUTF8',
259 b'MAIL from: <foo@example.com> size=20 SMTPUTF8 BODY=8BITMIME',
260 ]:
261 self.write_line(channel, line)
262 self.assertEqual(channel.socket.last, self.error_response)
263 self.write_line(
264 channel,
265 b'MAIL from: <foo@example.com> size=20 SMTPUTF8 BODY=UNKNOWN')
266 self.assertEqual(
267 channel.socket.last,
268 b'501 Error: BODY can only be one of 7BIT, 8BITMIME\r\n')
269 self.write_line(
270 channel, b'MAIL from: <foo@example.com> size=20 body=8bitmime')
271 self.assertEqual(channel.socket.last, b'250 OK\r\n')
272
273 def test_with_enable_smtputf8_true(self):
274 server = DummyServer((support.HOST, 0), ('b', 0), enable_SMTPUTF8=True)
275 conn, addr = server.accept()
276 channel = smtpd.SMTPChannel(server, conn, addr, enable_SMTPUTF8=True)
277 self.write_line(channel, b'EHLO example')
278 self.write_line(
279 channel,
280 b'MAIL from: <foo@example.com> size=20 body=8bitmime smtputf8')
281 self.assertEqual(channel.socket.last, b'250 OK\r\n')
282
283
R David Murrayd1a30c92012-05-26 14:33:59 -0400284class SMTPDChannelTest(unittest.TestCase):
Richard Jones8cb36192010-07-23 16:20:40 +0000285 def setUp(self):
Richard Jones64b02de2010-08-03 06:39:33 +0000286 smtpd.socket = asyncore.socket = mock_socket
Antoine Pitroue0815e22011-11-12 20:36:29 +0100287 self.old_debugstream = smtpd.DEBUGSTREAM
Richard Jones8cb36192010-07-23 16:20:40 +0000288 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
R David Murray2539e672014-08-09 16:40:49 -0400289 self.server = DummyServer((support.HOST, 0), ('b', 0),
290 decode_data=True)
Richard Jones8cb36192010-07-23 16:20:40 +0000291 conn, addr = self.server.accept()
R David Murray554bcbf2014-06-11 11:18:08 -0400292 self.channel = smtpd.SMTPChannel(self.server, conn, addr,
293 decode_data=True)
Richard Jones8cb36192010-07-23 16:20:40 +0000294
Richard Jones64b02de2010-08-03 06:39:33 +0000295 def tearDown(self):
Richard Jonesdaf23502010-08-16 01:48:14 +0000296 asyncore.close_all()
Richard Jones64b02de2010-08-03 06:39:33 +0000297 asyncore.socket = smtpd.socket = socket
Antoine Pitroue0815e22011-11-12 20:36:29 +0100298 smtpd.DEBUGSTREAM = self.old_debugstream
Richard Jones64b02de2010-08-03 06:39:33 +0000299
Richard Jones8cb36192010-07-23 16:20:40 +0000300 def write_line(self, line):
301 self.channel.socket.queue_recv(line)
302 self.channel.handle_read()
303
304 def test_broken_connect(self):
R David Murray6fe56a32014-06-11 13:48:58 -0400305 self.assertRaises(
306 DummyDispatcherBroken, BrokenDummyServer,
R David Murray2539e672014-08-09 16:40:49 -0400307 (support.HOST, 0), ('b', 0), decode_data=True)
Richard Jones8cb36192010-07-23 16:20:40 +0000308
R David Murray1a815382015-10-09 10:19:33 -0400309 def test_decode_data_and_enable_SMTPUTF8_raises(self):
310 self.assertRaises(
311 ValueError, smtpd.SMTPChannel,
312 self.server, self.channel.conn, self.channel.addr,
313 enable_SMTPUTF8=True, decode_data=True)
314
Richard Jones8cb36192010-07-23 16:20:40 +0000315 def test_server_accept(self):
316 self.server.handle_accept()
317
318 def test_missing_data(self):
319 self.write_line(b'')
Georg Brandl17e3d692010-07-31 10:08:09 +0000320 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000321 b'500 Error: bad syntax\r\n')
322
R David Murrayd1a30c92012-05-26 14:33:59 -0400323 def test_EHLO(self):
324 self.write_line(b'EHLO example')
325 self.assertEqual(self.channel.socket.last, b'250 HELP\r\n')
326
327 def test_EHLO_bad_syntax(self):
328 self.write_line(b'EHLO')
Georg Brandl17e3d692010-07-31 10:08:09 +0000329 self.assertEqual(self.channel.socket.last,
R David Murrayd1a30c92012-05-26 14:33:59 -0400330 b'501 Syntax: EHLO hostname\r\n')
331
332 def test_EHLO_duplicate(self):
333 self.write_line(b'EHLO example')
334 self.write_line(b'EHLO example')
335 self.assertEqual(self.channel.socket.last,
336 b'503 Duplicate HELO/EHLO\r\n')
337
338 def test_EHLO_HELO_duplicate(self):
339 self.write_line(b'EHLO example')
340 self.write_line(b'HELO example')
341 self.assertEqual(self.channel.socket.last,
342 b'503 Duplicate HELO/EHLO\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000343
344 def test_HELO(self):
Richard Jones64b02de2010-08-03 06:39:33 +0000345 name = smtpd.socket.getfqdn()
R David Murrayd1a30c92012-05-26 14:33:59 -0400346 self.write_line(b'HELO example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000347 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000348 '250 {}\r\n'.format(name).encode('ascii'))
349
R David Murrayd1a30c92012-05-26 14:33:59 -0400350 def test_HELO_EHLO_duplicate(self):
351 self.write_line(b'HELO example')
352 self.write_line(b'EHLO example')
353 self.assertEqual(self.channel.socket.last,
354 b'503 Duplicate HELO/EHLO\r\n')
355
356 def test_HELP(self):
357 self.write_line(b'HELP')
358 self.assertEqual(self.channel.socket.last,
359 b'250 Supported commands: EHLO HELO MAIL RCPT ' + \
360 b'DATA RSET NOOP QUIT VRFY\r\n')
361
362 def test_HELP_command(self):
363 self.write_line(b'HELP MAIL')
364 self.assertEqual(self.channel.socket.last,
365 b'250 Syntax: MAIL FROM: <address>\r\n')
366
367 def test_HELP_command_unknown(self):
368 self.write_line(b'HELP SPAM')
369 self.assertEqual(self.channel.socket.last,
370 b'501 Supported commands: EHLO HELO MAIL RCPT ' + \
371 b'DATA RSET NOOP QUIT VRFY\r\n')
372
Richard Jones8cb36192010-07-23 16:20:40 +0000373 def test_HELO_bad_syntax(self):
374 self.write_line(b'HELO')
Georg Brandl17e3d692010-07-31 10:08:09 +0000375 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000376 b'501 Syntax: HELO hostname\r\n')
377
378 def test_HELO_duplicate(self):
R David Murrayd1a30c92012-05-26 14:33:59 -0400379 self.write_line(b'HELO example')
380 self.write_line(b'HELO example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000381 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000382 b'503 Duplicate HELO/EHLO\r\n')
383
R David Murrayd1a30c92012-05-26 14:33:59 -0400384 def test_HELO_parameter_rejected_when_extensions_not_enabled(self):
385 self.extended_smtp = False
386 self.write_line(b'HELO example')
387 self.write_line(b'MAIL from:<foo@example.com> SIZE=1234')
388 self.assertEqual(self.channel.socket.last,
389 b'501 Syntax: MAIL FROM: <address>\r\n')
390
391 def test_MAIL_allows_space_after_colon(self):
392 self.write_line(b'HELO example')
393 self.write_line(b'MAIL from: <foo@example.com>')
394 self.assertEqual(self.channel.socket.last,
395 b'250 OK\r\n')
396
397 def test_extended_MAIL_allows_space_after_colon(self):
398 self.write_line(b'EHLO example')
399 self.write_line(b'MAIL from: <foo@example.com> size=20')
400 self.assertEqual(self.channel.socket.last,
401 b'250 OK\r\n')
402
Richard Jones8cb36192010-07-23 16:20:40 +0000403 def test_NOOP(self):
404 self.write_line(b'NOOP')
R David Murrayd1a30c92012-05-26 14:33:59 -0400405 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000406
R David Murray669b7552012-03-20 16:16:29 -0400407 def test_HELO_NOOP(self):
408 self.write_line(b'HELO example')
409 self.write_line(b'NOOP')
R David Murrayd1a30c92012-05-26 14:33:59 -0400410 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
R David Murray669b7552012-03-20 16:16:29 -0400411
Richard Jones8cb36192010-07-23 16:20:40 +0000412 def test_NOOP_bad_syntax(self):
413 self.write_line(b'NOOP hi')
Georg Brandl17e3d692010-07-31 10:08:09 +0000414 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000415 b'501 Syntax: NOOP\r\n')
416
417 def test_QUIT(self):
418 self.write_line(b'QUIT')
Georg Brandl17e3d692010-07-31 10:08:09 +0000419 self.assertEqual(self.channel.socket.last, b'221 Bye\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000420
R David Murray669b7552012-03-20 16:16:29 -0400421 def test_HELO_QUIT(self):
422 self.write_line(b'HELO example')
423 self.write_line(b'QUIT')
424 self.assertEqual(self.channel.socket.last, b'221 Bye\r\n')
425
Richard Jones8cb36192010-07-23 16:20:40 +0000426 def test_QUIT_arg_ignored(self):
427 self.write_line(b'QUIT bye bye')
Georg Brandl17e3d692010-07-31 10:08:09 +0000428 self.assertEqual(self.channel.socket.last, b'221 Bye\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000429
430 def test_bad_state(self):
Georg Brandl17e3d692010-07-31 10:08:09 +0000431 self.channel.smtp_state = 'BAD STATE'
R David Murray669b7552012-03-20 16:16:29 -0400432 self.write_line(b'HELO example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000433 self.assertEqual(self.channel.socket.last,
434 b'451 Internal confusion\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000435
Georg Brandl1e5c5f82010-12-03 07:38:22 +0000436 def test_command_too_long(self):
R David Murray669b7552012-03-20 16:16:29 -0400437 self.write_line(b'HELO example')
R David Murrayd1a30c92012-05-26 14:33:59 -0400438 self.write_line(b'MAIL from: ' +
Georg Brandl1e5c5f82010-12-03 07:38:22 +0000439 b'a' * self.channel.command_size_limit +
440 b'@example')
441 self.assertEqual(self.channel.socket.last,
442 b'500 Error: line too long\r\n')
443
R David Murrayd1a30c92012-05-26 14:33:59 -0400444 def test_MAIL_command_limit_extended_with_SIZE(self):
445 self.write_line(b'EHLO example')
446 fill_len = self.channel.command_size_limit - len('MAIL from:<@example>')
447 self.write_line(b'MAIL from:<' +
448 b'a' * fill_len +
449 b'@example> SIZE=1234')
450 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
451
452 self.write_line(b'MAIL from:<' +
453 b'a' * (fill_len + 26) +
454 b'@example> SIZE=1234')
455 self.assertEqual(self.channel.socket.last,
456 b'500 Error: line too long\r\n')
457
R David Murray2539e672014-08-09 16:40:49 -0400458 def test_MAIL_command_rejects_SMTPUTF8_by_default(self):
459 self.write_line(b'EHLO example')
460 self.write_line(
461 b'MAIL from: <naive@example.com> BODY=8BITMIME SMTPUTF8')
462 self.assertEqual(self.channel.socket.last[0:1], b'5')
463
R David Murrayd1a30c92012-05-26 14:33:59 -0400464 def test_data_longer_than_default_data_size_limit(self):
465 # Hack the default so we don't have to generate so much data.
466 self.channel.data_size_limit = 1048
R David Murray669b7552012-03-20 16:16:29 -0400467 self.write_line(b'HELO example')
Georg Brandl1e5c5f82010-12-03 07:38:22 +0000468 self.write_line(b'MAIL From:eggs@example')
469 self.write_line(b'RCPT To:spam@example')
470 self.write_line(b'DATA')
471 self.write_line(b'A' * self.channel.data_size_limit +
472 b'A\r\n.')
473 self.assertEqual(self.channel.socket.last,
474 b'552 Error: Too much mail data\r\n')
475
R David Murrayd1a30c92012-05-26 14:33:59 -0400476 def test_MAIL_size_parameter(self):
477 self.write_line(b'EHLO example')
478 self.write_line(b'MAIL FROM:<eggs@example> SIZE=512')
479 self.assertEqual(self.channel.socket.last,
480 b'250 OK\r\n')
481
482 def test_MAIL_invalid_size_parameter(self):
483 self.write_line(b'EHLO example')
484 self.write_line(b'MAIL FROM:<eggs@example> SIZE=invalid')
485 self.assertEqual(self.channel.socket.last,
486 b'501 Syntax: MAIL FROM: <address> [SP <mail-parameters>]\r\n')
487
488 def test_MAIL_RCPT_unknown_parameters(self):
489 self.write_line(b'EHLO example')
490 self.write_line(b'MAIL FROM:<eggs@example> ham=green')
491 self.assertEqual(self.channel.socket.last,
492 b'555 MAIL FROM parameters not recognized or not implemented\r\n')
493
494 self.write_line(b'MAIL FROM:<eggs@example>')
495 self.write_line(b'RCPT TO:<eggs@example> ham=green')
496 self.assertEqual(self.channel.socket.last,
497 b'555 RCPT TO parameters not recognized or not implemented\r\n')
498
499 def test_MAIL_size_parameter_larger_than_default_data_size_limit(self):
500 self.channel.data_size_limit = 1048
501 self.write_line(b'EHLO example')
502 self.write_line(b'MAIL FROM:<eggs@example> SIZE=2096')
503 self.assertEqual(self.channel.socket.last,
504 b'552 Error: message size exceeds fixed maximum message size\r\n')
505
Richard Jones8cb36192010-07-23 16:20:40 +0000506 def test_need_MAIL(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'RCPT to:spam@example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000509 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000510 b'503 Error: need MAIL command\r\n')
511
R David Murrayd1a30c92012-05-26 14:33:59 -0400512 def test_MAIL_syntax_HELO(self):
R David Murray669b7552012-03-20 16:16:29 -0400513 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000514 self.write_line(b'MAIL from eggs@example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000515 self.assertEqual(self.channel.socket.last,
R David Murrayd1a30c92012-05-26 14:33:59 -0400516 b'501 Syntax: MAIL FROM: <address>\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000517
R David Murrayd1a30c92012-05-26 14:33:59 -0400518 def test_MAIL_syntax_EHLO(self):
519 self.write_line(b'EHLO example')
520 self.write_line(b'MAIL from eggs@example')
521 self.assertEqual(self.channel.socket.last,
522 b'501 Syntax: MAIL FROM: <address> [SP <mail-parameters>]\r\n')
523
524 def test_MAIL_missing_address(self):
R David Murray669b7552012-03-20 16:16:29 -0400525 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000526 self.write_line(b'MAIL from:')
Georg Brandl17e3d692010-07-31 10:08:09 +0000527 self.assertEqual(self.channel.socket.last,
R David Murrayd1a30c92012-05-26 14:33:59 -0400528 b'501 Syntax: MAIL FROM: <address>\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000529
530 def test_MAIL_chevrons(self):
R David Murray669b7552012-03-20 16:16:29 -0400531 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000532 self.write_line(b'MAIL from:<eggs@example>')
R David Murrayd1a30c92012-05-26 14:33:59 -0400533 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
534
535 def test_MAIL_empty_chevrons(self):
536 self.write_line(b'EHLO example')
537 self.write_line(b'MAIL from:<>')
538 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
539
540 def test_MAIL_quoted_localpart(self):
541 self.write_line(b'EHLO example')
542 self.write_line(b'MAIL from: <"Fred Blogs"@example.com>')
543 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
544 self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
545
546 def test_MAIL_quoted_localpart_no_angles(self):
547 self.write_line(b'EHLO example')
548 self.write_line(b'MAIL from: "Fred Blogs"@example.com')
549 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
550 self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
551
552 def test_MAIL_quoted_localpart_with_size(self):
553 self.write_line(b'EHLO example')
554 self.write_line(b'MAIL from: <"Fred Blogs"@example.com> SIZE=1000')
555 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
556 self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
557
558 def test_MAIL_quoted_localpart_with_size_no_angles(self):
559 self.write_line(b'EHLO example')
560 self.write_line(b'MAIL from: "Fred Blogs"@example.com SIZE=1000')
561 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
562 self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
Richard Jones8cb36192010-07-23 16:20:40 +0000563
564 def test_nested_MAIL(self):
R David Murray669b7552012-03-20 16:16:29 -0400565 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000566 self.write_line(b'MAIL from:eggs@example')
567 self.write_line(b'MAIL from:spam@example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000568 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000569 b'503 Error: nested MAIL command\r\n')
570
R David Murrayd1a30c92012-05-26 14:33:59 -0400571 def test_VRFY(self):
572 self.write_line(b'VRFY eggs@example')
573 self.assertEqual(self.channel.socket.last,
574 b'252 Cannot VRFY user, but will accept message and attempt ' + \
575 b'delivery\r\n')
576
577 def test_VRFY_syntax(self):
578 self.write_line(b'VRFY')
579 self.assertEqual(self.channel.socket.last,
580 b'501 Syntax: VRFY <address>\r\n')
581
582 def test_EXPN_not_implemented(self):
583 self.write_line(b'EXPN')
584 self.assertEqual(self.channel.socket.last,
585 b'502 EXPN not implemented\r\n')
586
R David Murray669b7552012-03-20 16:16:29 -0400587 def test_no_HELO_MAIL(self):
588 self.write_line(b'MAIL from:<foo@example.com>')
589 self.assertEqual(self.channel.socket.last,
590 b'503 Error: send HELO first\r\n')
591
Richard Jones8cb36192010-07-23 16:20:40 +0000592 def test_need_RCPT(self):
R David Murray669b7552012-03-20 16:16:29 -0400593 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000594 self.write_line(b'MAIL From:eggs@example')
595 self.write_line(b'DATA')
Georg Brandl17e3d692010-07-31 10:08:09 +0000596 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000597 b'503 Error: need RCPT command\r\n')
598
R David Murrayd1a30c92012-05-26 14:33:59 -0400599 def test_RCPT_syntax_HELO(self):
R David Murray669b7552012-03-20 16:16:29 -0400600 self.write_line(b'HELO example')
R David Murrayd1a30c92012-05-26 14:33:59 -0400601 self.write_line(b'MAIL From: eggs@example')
Richard Jones8cb36192010-07-23 16:20:40 +0000602 self.write_line(b'RCPT to eggs@example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000603 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000604 b'501 Syntax: RCPT TO: <address>\r\n')
605
R David Murrayd1a30c92012-05-26 14:33:59 -0400606 def test_RCPT_syntax_EHLO(self):
607 self.write_line(b'EHLO example')
608 self.write_line(b'MAIL From: eggs@example')
609 self.write_line(b'RCPT to eggs@example')
610 self.assertEqual(self.channel.socket.last,
611 b'501 Syntax: RCPT TO: <address> [SP <mail-parameters>]\r\n')
612
613 def test_RCPT_lowercase_to_OK(self):
614 self.write_line(b'HELO example')
615 self.write_line(b'MAIL From: eggs@example')
616 self.write_line(b'RCPT to: <eggs@example>')
617 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
618
R David Murray669b7552012-03-20 16:16:29 -0400619 def test_no_HELO_RCPT(self):
620 self.write_line(b'RCPT to eggs@example')
621 self.assertEqual(self.channel.socket.last,
622 b'503 Error: send HELO first\r\n')
623
Richard Jones8cb36192010-07-23 16:20:40 +0000624 def test_data_dialog(self):
R David Murray669b7552012-03-20 16:16:29 -0400625 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000626 self.write_line(b'MAIL From:eggs@example')
R David Murrayd1a30c92012-05-26 14:33:59 -0400627 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000628 self.write_line(b'RCPT To:spam@example')
R David Murrayd1a30c92012-05-26 14:33:59 -0400629 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000630
631 self.write_line(b'DATA')
Georg Brandl17e3d692010-07-31 10:08:09 +0000632 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000633 b'354 End data with <CR><LF>.<CR><LF>\r\n')
634 self.write_line(b'data\r\nmore\r\n.')
R David Murrayd1a30c92012-05-26 14:33:59 -0400635 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000636 self.assertEqual(self.server.messages,
R David Murray2539e672014-08-09 16:40:49 -0400637 [(('peer-address', 'peer-port'),
638 'eggs@example',
639 ['spam@example'],
640 'data\nmore')])
Richard Jones8cb36192010-07-23 16:20:40 +0000641
642 def test_DATA_syntax(self):
R David Murray669b7552012-03-20 16:16:29 -0400643 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000644 self.write_line(b'MAIL From:eggs@example')
645 self.write_line(b'RCPT To:spam@example')
646 self.write_line(b'DATA spam')
Georg Brandl17e3d692010-07-31 10:08:09 +0000647 self.assertEqual(self.channel.socket.last, b'501 Syntax: DATA\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000648
R David Murray669b7552012-03-20 16:16:29 -0400649 def test_no_HELO_DATA(self):
650 self.write_line(b'DATA spam')
651 self.assertEqual(self.channel.socket.last,
652 b'503 Error: send HELO first\r\n')
653
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000654 def test_data_transparency_section_4_5_2(self):
R David Murray669b7552012-03-20 16:16:29 -0400655 self.write_line(b'HELO example')
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000656 self.write_line(b'MAIL From:eggs@example')
657 self.write_line(b'RCPT To:spam@example')
658 self.write_line(b'DATA')
659 self.write_line(b'..\r\n.\r\n')
660 self.assertEqual(self.channel.received_data, '.')
661
Richard Jones8cb36192010-07-23 16:20:40 +0000662 def test_multiple_RCPT(self):
R David Murray669b7552012-03-20 16:16:29 -0400663 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000664 self.write_line(b'MAIL From:eggs@example')
665 self.write_line(b'RCPT To:spam@example')
666 self.write_line(b'RCPT To:ham@example')
667 self.write_line(b'DATA')
668 self.write_line(b'data\r\n.')
Richard Jones6a9e6bb2010-08-04 12:27:36 +0000669 self.assertEqual(self.server.messages,
R David Murray2539e672014-08-09 16:40:49 -0400670 [(('peer-address', 'peer-port'),
671 'eggs@example',
672 ['spam@example','ham@example'],
673 'data')])
Richard Jones8cb36192010-07-23 16:20:40 +0000674
675 def test_manual_status(self):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000676 # checks that the Channel is able to return a custom status message
R David Murray669b7552012-03-20 16:16:29 -0400677 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000678 self.write_line(b'MAIL From:eggs@example')
679 self.write_line(b'RCPT To:spam@example')
680 self.write_line(b'DATA')
681 self.write_line(b'return status\r\n.')
Georg Brandl17e3d692010-07-31 10:08:09 +0000682 self.assertEqual(self.channel.socket.last, b'250 Okish\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000683
684 def test_RSET(self):
R David Murray669b7552012-03-20 16:16:29 -0400685 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000686 self.write_line(b'MAIL From:eggs@example')
687 self.write_line(b'RCPT To:spam@example')
688 self.write_line(b'RSET')
R David Murrayd1a30c92012-05-26 14:33:59 -0400689 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000690 self.write_line(b'MAIL From:foo@example')
691 self.write_line(b'RCPT To:eggs@example')
692 self.write_line(b'DATA')
693 self.write_line(b'data\r\n.')
Richard Jones6a9e6bb2010-08-04 12:27:36 +0000694 self.assertEqual(self.server.messages,
R David Murray2539e672014-08-09 16:40:49 -0400695 [(('peer-address', 'peer-port'),
696 'foo@example',
697 ['eggs@example'],
698 'data')])
Richard Jones8cb36192010-07-23 16:20:40 +0000699
R David Murray669b7552012-03-20 16:16:29 -0400700 def test_HELO_RSET(self):
701 self.write_line(b'HELO example')
702 self.write_line(b'RSET')
R David Murrayd1a30c92012-05-26 14:33:59 -0400703 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
R David Murray669b7552012-03-20 16:16:29 -0400704
Richard Jones8cb36192010-07-23 16:20:40 +0000705 def test_RSET_syntax(self):
706 self.write_line(b'RSET hi')
Georg Brandl17e3d692010-07-31 10:08:09 +0000707 self.assertEqual(self.channel.socket.last, b'501 Syntax: RSET\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000708
R David Murrayd1a30c92012-05-26 14:33:59 -0400709 def test_unknown_command(self):
710 self.write_line(b'UNKNOWN_CMD')
711 self.assertEqual(self.channel.socket.last,
712 b'500 Error: command "UNKNOWN_CMD" not ' + \
713 b'recognized\r\n')
714
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000715 def test_attribute_deprecations(self):
Florent Xicluna67317752011-12-10 11:07:42 +0100716 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000717 spam = self.channel._SMTPChannel__server
Florent Xicluna67317752011-12-10 11:07:42 +0100718 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000719 self.channel._SMTPChannel__server = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100720 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000721 spam = self.channel._SMTPChannel__line
Florent Xicluna67317752011-12-10 11:07:42 +0100722 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000723 self.channel._SMTPChannel__line = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100724 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000725 spam = self.channel._SMTPChannel__state
Florent Xicluna67317752011-12-10 11:07:42 +0100726 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000727 self.channel._SMTPChannel__state = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100728 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000729 spam = self.channel._SMTPChannel__greeting
Florent Xicluna67317752011-12-10 11:07:42 +0100730 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000731 self.channel._SMTPChannel__greeting = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100732 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000733 spam = self.channel._SMTPChannel__mailfrom
Florent Xicluna67317752011-12-10 11:07:42 +0100734 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000735 self.channel._SMTPChannel__mailfrom = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100736 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000737 spam = self.channel._SMTPChannel__rcpttos
Florent Xicluna67317752011-12-10 11:07:42 +0100738 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000739 self.channel._SMTPChannel__rcpttos = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100740 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000741 spam = self.channel._SMTPChannel__data
Florent Xicluna67317752011-12-10 11:07:42 +0100742 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000743 self.channel._SMTPChannel__data = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100744 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000745 spam = self.channel._SMTPChannel__fqdn
Florent Xicluna67317752011-12-10 11:07:42 +0100746 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000747 self.channel._SMTPChannel__fqdn = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100748 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000749 spam = self.channel._SMTPChannel__peer
Florent Xicluna67317752011-12-10 11:07:42 +0100750 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000751 self.channel._SMTPChannel__peer = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100752 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000753 spam = self.channel._SMTPChannel__conn
Florent Xicluna67317752011-12-10 11:07:42 +0100754 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000755 self.channel._SMTPChannel__conn = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100756 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000757 spam = self.channel._SMTPChannel__addr
Florent Xicluna67317752011-12-10 11:07:42 +0100758 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000759 self.channel._SMTPChannel__addr = 'spam'
Richard Jones8cb36192010-07-23 16:20:40 +0000760
R David Murray6fe56a32014-06-11 13:48:58 -0400761@unittest.skipUnless(support.IPV6_ENABLED, "IPv6 not enabled")
762class SMTPDChannelIPv6Test(SMTPDChannelTest):
763 def setUp(self):
764 smtpd.socket = asyncore.socket = mock_socket
765 self.old_debugstream = smtpd.DEBUGSTREAM
766 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
R David Murray2539e672014-08-09 16:40:49 -0400767 self.server = DummyServer((support.HOSTv6, 0), ('b', 0),
768 decode_data=True)
R David Murray6fe56a32014-06-11 13:48:58 -0400769 conn, addr = self.server.accept()
770 self.channel = smtpd.SMTPChannel(self.server, conn, addr,
771 decode_data=True)
R David Murrayd1a30c92012-05-26 14:33:59 -0400772
773class SMTPDChannelWithDataSizeLimitTest(unittest.TestCase):
774
775 def setUp(self):
776 smtpd.socket = asyncore.socket = mock_socket
R David Murray05cab752012-06-04 15:55:51 -0400777 self.old_debugstream = smtpd.DEBUGSTREAM
R David Murrayd1a30c92012-05-26 14:33:59 -0400778 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
R David Murray2539e672014-08-09 16:40:49 -0400779 self.server = DummyServer((support.HOST, 0), ('b', 0),
780 decode_data=True)
R David Murrayd1a30c92012-05-26 14:33:59 -0400781 conn, addr = self.server.accept()
782 # Set DATA size limit to 32 bytes for easy testing
R David Murray554bcbf2014-06-11 11:18:08 -0400783 self.channel = smtpd.SMTPChannel(self.server, conn, addr, 32,
784 decode_data=True)
R David Murrayd1a30c92012-05-26 14:33:59 -0400785
786 def tearDown(self):
787 asyncore.close_all()
788 asyncore.socket = smtpd.socket = socket
R David Murray05cab752012-06-04 15:55:51 -0400789 smtpd.DEBUGSTREAM = self.old_debugstream
R David Murrayd1a30c92012-05-26 14:33:59 -0400790
791 def write_line(self, line):
792 self.channel.socket.queue_recv(line)
793 self.channel.handle_read()
794
795 def test_data_limit_dialog(self):
796 self.write_line(b'HELO example')
797 self.write_line(b'MAIL From:eggs@example')
798 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
799 self.write_line(b'RCPT To:spam@example')
800 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
801
802 self.write_line(b'DATA')
803 self.assertEqual(self.channel.socket.last,
804 b'354 End data with <CR><LF>.<CR><LF>\r\n')
805 self.write_line(b'data\r\nmore\r\n.')
806 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
807 self.assertEqual(self.server.messages,
R David Murray2539e672014-08-09 16:40:49 -0400808 [(('peer-address', 'peer-port'),
809 'eggs@example',
810 ['spam@example'],
811 'data\nmore')])
R David Murrayd1a30c92012-05-26 14:33:59 -0400812
813 def test_data_limit_dialog_too_much_data(self):
814 self.write_line(b'HELO example')
815 self.write_line(b'MAIL From:eggs@example')
816 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
817 self.write_line(b'RCPT To:spam@example')
818 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
819
820 self.write_line(b'DATA')
821 self.assertEqual(self.channel.socket.last,
822 b'354 End data with <CR><LF>.<CR><LF>\r\n')
823 self.write_line(b'This message is longer than 32 bytes\r\n.')
824 self.assertEqual(self.channel.socket.last,
825 b'552 Error: Too much mail data\r\n')
826
Richard Jones8cb36192010-07-23 16:20:40 +0000827
R David Murray554bcbf2014-06-11 11:18:08 -0400828class SMTPDChannelWithDecodeDataFalse(unittest.TestCase):
829
830 def setUp(self):
831 smtpd.socket = asyncore.socket = mock_socket
832 self.old_debugstream = smtpd.DEBUGSTREAM
833 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
Serhiy Storchakacbcc2fd2016-05-16 09:36:31 +0300834 self.server = DummyServer((support.HOST, 0), ('b', 0))
R David Murray554bcbf2014-06-11 11:18:08 -0400835 conn, addr = self.server.accept()
Serhiy Storchakacbcc2fd2016-05-16 09:36:31 +0300836 self.channel = smtpd.SMTPChannel(self.server, conn, addr)
R David Murray554bcbf2014-06-11 11:18:08 -0400837
838 def tearDown(self):
839 asyncore.close_all()
840 asyncore.socket = smtpd.socket = socket
841 smtpd.DEBUGSTREAM = self.old_debugstream
842
843 def write_line(self, line):
844 self.channel.socket.queue_recv(line)
845 self.channel.handle_read()
846
847 def test_ascii_data(self):
848 self.write_line(b'HELO example')
849 self.write_line(b'MAIL From:eggs@example')
850 self.write_line(b'RCPT To:spam@example')
851 self.write_line(b'DATA')
852 self.write_line(b'plain ascii text')
853 self.write_line(b'.')
854 self.assertEqual(self.channel.received_data, b'plain ascii text')
855
856 def test_utf8_data(self):
857 self.write_line(b'HELO example')
858 self.write_line(b'MAIL From:eggs@example')
859 self.write_line(b'RCPT To:spam@example')
860 self.write_line(b'DATA')
861 self.write_line(b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87')
862 self.write_line(b'and some plain ascii')
863 self.write_line(b'.')
864 self.assertEqual(
865 self.channel.received_data,
866 b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87\n'
867 b'and some plain ascii')
868
869
870class SMTPDChannelWithDecodeDataTrue(unittest.TestCase):
871
872 def setUp(self):
873 smtpd.socket = asyncore.socket = mock_socket
874 self.old_debugstream = smtpd.DEBUGSTREAM
875 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
R David Murray6fe56a32014-06-11 13:48:58 -0400876 self.server = DummyServer((support.HOST, 0), ('b', 0),
877 decode_data=True)
R David Murray554bcbf2014-06-11 11:18:08 -0400878 conn, addr = self.server.accept()
879 # Set decode_data to True
880 self.channel = smtpd.SMTPChannel(self.server, conn, addr,
881 decode_data=True)
882
883 def tearDown(self):
884 asyncore.close_all()
885 asyncore.socket = smtpd.socket = socket
886 smtpd.DEBUGSTREAM = self.old_debugstream
887
888 def write_line(self, line):
889 self.channel.socket.queue_recv(line)
890 self.channel.handle_read()
891
892 def test_ascii_data(self):
893 self.write_line(b'HELO example')
894 self.write_line(b'MAIL From:eggs@example')
895 self.write_line(b'RCPT To:spam@example')
896 self.write_line(b'DATA')
897 self.write_line(b'plain ascii text')
898 self.write_line(b'.')
899 self.assertEqual(self.channel.received_data, 'plain ascii text')
900
901 def test_utf8_data(self):
902 self.write_line(b'HELO example')
903 self.write_line(b'MAIL From:eggs@example')
904 self.write_line(b'RCPT To:spam@example')
905 self.write_line(b'DATA')
906 self.write_line(b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87')
907 self.write_line(b'and some plain ascii')
908 self.write_line(b'.')
909 self.assertEqual(
910 self.channel.received_data,
911 'utf8 enriched text: żźć\nand some plain ascii')
912
913
R David Murray2539e672014-08-09 16:40:49 -0400914class SMTPDChannelTestWithEnableSMTPUTF8True(unittest.TestCase):
915 def setUp(self):
916 smtpd.socket = asyncore.socket = mock_socket
917 self.old_debugstream = smtpd.DEBUGSTREAM
918 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
919 self.server = DummyServer((support.HOST, 0), ('b', 0),
920 enable_SMTPUTF8=True)
921 conn, addr = self.server.accept()
922 self.channel = smtpd.SMTPChannel(self.server, conn, addr,
923 enable_SMTPUTF8=True)
924
925 def tearDown(self):
926 asyncore.close_all()
927 asyncore.socket = smtpd.socket = socket
928 smtpd.DEBUGSTREAM = self.old_debugstream
929
930 def write_line(self, line):
931 self.channel.socket.queue_recv(line)
932 self.channel.handle_read()
933
934 def test_MAIL_command_accepts_SMTPUTF8_when_announced(self):
935 self.write_line(b'EHLO example')
936 self.write_line(
937 'MAIL from: <naïve@example.com> BODY=8BITMIME SMTPUTF8'.encode(
938 'utf-8')
939 )
940 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
941
942 def test_process_smtputf8_message(self):
943 self.write_line(b'EHLO example')
944 for mail_parameters in [b'', b'BODY=8BITMIME SMTPUTF8']:
945 self.write_line(b'MAIL from: <a@example> ' + mail_parameters)
946 self.assertEqual(self.channel.socket.last[0:3], b'250')
947 self.write_line(b'rcpt to:<b@example.com>')
948 self.assertEqual(self.channel.socket.last[0:3], b'250')
949 self.write_line(b'data')
950 self.assertEqual(self.channel.socket.last[0:3], b'354')
951 self.write_line(b'c\r\n.')
952 if mail_parameters == b'':
953 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
954 else:
955 self.assertEqual(self.channel.socket.last,
956 b'250 SMTPUTF8 message okish\r\n')
957
958 def test_utf8_data(self):
959 self.write_line(b'EHLO example')
960 self.write_line(
961 'MAIL From: naïve@examplé BODY=8BITMIME SMTPUTF8'.encode('utf-8'))
962 self.assertEqual(self.channel.socket.last[0:3], b'250')
963 self.write_line('RCPT To:späm@examplé'.encode('utf-8'))
964 self.assertEqual(self.channel.socket.last[0:3], b'250')
965 self.write_line(b'DATA')
966 self.assertEqual(self.channel.socket.last[0:3], b'354')
967 self.write_line(b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87')
968 self.write_line(b'.')
969 self.assertEqual(
970 self.channel.received_data,
971 b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87')
972
973 def test_MAIL_command_limit_extended_with_SIZE_and_SMTPUTF8(self):
974 self.write_line(b'ehlo example')
975 fill_len = (512 + 26 + 10) - len('mail from:<@example>')
976 self.write_line(b'MAIL from:<' +
977 b'a' * (fill_len + 1) +
978 b'@example>')
979 self.assertEqual(self.channel.socket.last,
980 b'500 Error: line too long\r\n')
981 self.write_line(b'MAIL from:<' +
982 b'a' * fill_len +
983 b'@example>')
984 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
985
986 def test_multiple_emails_with_extended_command_length(self):
987 self.write_line(b'ehlo example')
988 fill_len = (512 + 26 + 10) - len('mail from:<@example>')
989 for char in [b'a', b'b', b'c']:
990 self.write_line(b'MAIL from:<' + char * fill_len + b'a@example>')
991 self.assertEqual(self.channel.socket.last[0:3], b'500')
992 self.write_line(b'MAIL from:<' + char * fill_len + b'@example>')
993 self.assertEqual(self.channel.socket.last[0:3], b'250')
994 self.write_line(b'rcpt to:<hans@example.com>')
995 self.assertEqual(self.channel.socket.last[0:3], b'250')
996 self.write_line(b'data')
997 self.assertEqual(self.channel.socket.last[0:3], b'354')
998 self.write_line(b'test\r\n.')
999 self.assertEqual(self.channel.socket.last[0:3], b'250')
1000
Martin Panter380ef012016-06-06 02:03:11 +00001001
1002class MiscTestCase(unittest.TestCase):
1003 def test__all__(self):
1004 blacklist = {
1005 "program", "Devnull", "DEBUGSTREAM", "NEWLINE", "COMMASPACE",
1006 "DATA_SIZE_DEFAULT", "usage", "Options", "parseargs",
1007
1008 }
1009 support.check__all__(self, smtpd, blacklist=blacklist)
1010
1011
Richard Jones8cb36192010-07-23 16:20:40 +00001012if __name__ == "__main__":
R David Murrayd1a30c92012-05-26 14:33:59 -04001013 unittest.main()