blob: 93f14c4562bccefcb77adb16126067ca1ee97aee [file] [log] [blame]
R David Murrayd1a30c92012-05-26 14:33:59 -04001import unittest
Richard Jones64b02de2010-08-03 06:39:33 +00002from test import support, mock_socket
Richard Jones8cb36192010-07-23 16:20:40 +00003import socket
Richard Jones8cb36192010-07-23 16:20:40 +00004import io
5import smtpd
Richard Jones64b02de2010-08-03 06:39:33 +00006import asyncore
Richard Jones8cb36192010-07-23 16:20:40 +00007
Richard Jones8cb36192010-07-23 16:20:40 +00008
9class DummyServer(smtpd.SMTPServer):
Richard Jones4aa0d4d2010-08-04 01:20:14 +000010 def __init__(self, localaddr, remoteaddr):
11 smtpd.SMTPServer.__init__(self, localaddr, remoteaddr)
Georg Brandl6d23c442010-07-29 13:19:42 +000012 self.messages = []
Richard Jones64b02de2010-08-03 06:39:33 +000013
Richard Jones8cb36192010-07-23 16:20:40 +000014 def process_message(self, peer, mailfrom, rcpttos, data):
15 self.messages.append((peer, mailfrom, rcpttos, data))
16 if data == 'return status':
17 return '250 Okish'
18
Richard Jones4aa0d4d2010-08-04 01:20:14 +000019
Richard Jones8cb36192010-07-23 16:20:40 +000020class DummyDispatcherBroken(Exception):
21 pass
22
Richard Jones4aa0d4d2010-08-04 01:20:14 +000023
Richard Jones8cb36192010-07-23 16:20:40 +000024class BrokenDummyServer(DummyServer):
25 def listen(self, num):
26 raise DummyDispatcherBroken()
27
Richard Jones4aa0d4d2010-08-04 01:20:14 +000028
R David Murrayd1a30c92012-05-26 14:33:59 -040029class SMTPDServerTest(unittest.TestCase):
Richard Jones4aa0d4d2010-08-04 01:20:14 +000030 def setUp(self):
31 smtpd.socket = asyncore.socket = mock_socket
32
33 def test_process_message_unimplemented(self):
34 server = smtpd.SMTPServer('a', 'b')
35 conn, addr = server.accept()
36 channel = smtpd.SMTPChannel(server, conn, addr)
37
38 def write_line(line):
39 channel.socket.queue_recv(line)
40 channel.handle_read()
41
R David Murrayd1a30c92012-05-26 14:33:59 -040042 write_line(b'HELO example')
Richard Jones4aa0d4d2010-08-04 01:20:14 +000043 write_line(b'MAIL From:eggs@example')
44 write_line(b'RCPT To:spam@example')
45 write_line(b'DATA')
46 self.assertRaises(NotImplementedError, write_line, b'spam\r\n.\r\n')
47
48 def tearDown(self):
Richard Jonesdaf23502010-08-16 01:48:14 +000049 asyncore.close_all()
Richard Jones4aa0d4d2010-08-04 01:20:14 +000050 asyncore.socket = smtpd.socket = socket
51
52
R David Murrayd1a30c92012-05-26 14:33:59 -040053class SMTPDChannelTest(unittest.TestCase):
Richard Jones8cb36192010-07-23 16:20:40 +000054 def setUp(self):
Richard Jones64b02de2010-08-03 06:39:33 +000055 smtpd.socket = asyncore.socket = mock_socket
Antoine Pitroue0815e22011-11-12 20:36:29 +010056 self.old_debugstream = smtpd.DEBUGSTREAM
Richard Jones8cb36192010-07-23 16:20:40 +000057 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
58 self.server = DummyServer('a', 'b')
59 conn, addr = self.server.accept()
60 self.channel = smtpd.SMTPChannel(self.server, conn, addr)
61
Richard Jones64b02de2010-08-03 06:39:33 +000062 def tearDown(self):
Richard Jonesdaf23502010-08-16 01:48:14 +000063 asyncore.close_all()
Richard Jones64b02de2010-08-03 06:39:33 +000064 asyncore.socket = smtpd.socket = socket
Antoine Pitroue0815e22011-11-12 20:36:29 +010065 smtpd.DEBUGSTREAM = self.old_debugstream
Richard Jones64b02de2010-08-03 06:39:33 +000066
Richard Jones8cb36192010-07-23 16:20:40 +000067 def write_line(self, line):
68 self.channel.socket.queue_recv(line)
69 self.channel.handle_read()
70
71 def test_broken_connect(self):
72 self.assertRaises(DummyDispatcherBroken, BrokenDummyServer, 'a', 'b')
73
74 def test_server_accept(self):
75 self.server.handle_accept()
76
77 def test_missing_data(self):
78 self.write_line(b'')
Georg Brandl17e3d692010-07-31 10:08:09 +000079 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +000080 b'500 Error: bad syntax\r\n')
81
R David Murrayd1a30c92012-05-26 14:33:59 -040082 def test_EHLO(self):
83 self.write_line(b'EHLO example')
84 self.assertEqual(self.channel.socket.last, b'250 HELP\r\n')
85
86 def test_EHLO_bad_syntax(self):
87 self.write_line(b'EHLO')
Georg Brandl17e3d692010-07-31 10:08:09 +000088 self.assertEqual(self.channel.socket.last,
R David Murrayd1a30c92012-05-26 14:33:59 -040089 b'501 Syntax: EHLO hostname\r\n')
90
91 def test_EHLO_duplicate(self):
92 self.write_line(b'EHLO example')
93 self.write_line(b'EHLO example')
94 self.assertEqual(self.channel.socket.last,
95 b'503 Duplicate HELO/EHLO\r\n')
96
97 def test_EHLO_HELO_duplicate(self):
98 self.write_line(b'EHLO example')
99 self.write_line(b'HELO example')
100 self.assertEqual(self.channel.socket.last,
101 b'503 Duplicate HELO/EHLO\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000102
103 def test_HELO(self):
Richard Jones64b02de2010-08-03 06:39:33 +0000104 name = smtpd.socket.getfqdn()
R David Murrayd1a30c92012-05-26 14:33:59 -0400105 self.write_line(b'HELO example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000106 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000107 '250 {}\r\n'.format(name).encode('ascii'))
108
R David Murrayd1a30c92012-05-26 14:33:59 -0400109 def test_HELO_EHLO_duplicate(self):
110 self.write_line(b'HELO example')
111 self.write_line(b'EHLO example')
112 self.assertEqual(self.channel.socket.last,
113 b'503 Duplicate HELO/EHLO\r\n')
114
115 def test_HELP(self):
116 self.write_line(b'HELP')
117 self.assertEqual(self.channel.socket.last,
118 b'250 Supported commands: EHLO HELO MAIL RCPT ' + \
119 b'DATA RSET NOOP QUIT VRFY\r\n')
120
121 def test_HELP_command(self):
122 self.write_line(b'HELP MAIL')
123 self.assertEqual(self.channel.socket.last,
124 b'250 Syntax: MAIL FROM: <address>\r\n')
125
126 def test_HELP_command_unknown(self):
127 self.write_line(b'HELP SPAM')
128 self.assertEqual(self.channel.socket.last,
129 b'501 Supported commands: EHLO HELO MAIL RCPT ' + \
130 b'DATA RSET NOOP QUIT VRFY\r\n')
131
Richard Jones8cb36192010-07-23 16:20:40 +0000132 def test_HELO_bad_syntax(self):
133 self.write_line(b'HELO')
Georg Brandl17e3d692010-07-31 10:08:09 +0000134 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000135 b'501 Syntax: HELO hostname\r\n')
136
137 def test_HELO_duplicate(self):
R David Murrayd1a30c92012-05-26 14:33:59 -0400138 self.write_line(b'HELO example')
139 self.write_line(b'HELO example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000140 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000141 b'503 Duplicate HELO/EHLO\r\n')
142
R David Murrayd1a30c92012-05-26 14:33:59 -0400143 def test_HELO_parameter_rejected_when_extensions_not_enabled(self):
144 self.extended_smtp = False
145 self.write_line(b'HELO example')
146 self.write_line(b'MAIL from:<foo@example.com> SIZE=1234')
147 self.assertEqual(self.channel.socket.last,
148 b'501 Syntax: MAIL FROM: <address>\r\n')
149
150 def test_MAIL_allows_space_after_colon(self):
151 self.write_line(b'HELO example')
152 self.write_line(b'MAIL from: <foo@example.com>')
153 self.assertEqual(self.channel.socket.last,
154 b'250 OK\r\n')
155
156 def test_extended_MAIL_allows_space_after_colon(self):
157 self.write_line(b'EHLO example')
158 self.write_line(b'MAIL from: <foo@example.com> size=20')
159 self.assertEqual(self.channel.socket.last,
160 b'250 OK\r\n')
161
Richard Jones8cb36192010-07-23 16:20:40 +0000162 def test_NOOP(self):
163 self.write_line(b'NOOP')
R David Murrayd1a30c92012-05-26 14:33:59 -0400164 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000165
R David Murray669b7552012-03-20 16:16:29 -0400166 def test_HELO_NOOP(self):
167 self.write_line(b'HELO example')
168 self.write_line(b'NOOP')
R David Murrayd1a30c92012-05-26 14:33:59 -0400169 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
R David Murray669b7552012-03-20 16:16:29 -0400170
Richard Jones8cb36192010-07-23 16:20:40 +0000171 def test_NOOP_bad_syntax(self):
172 self.write_line(b'NOOP hi')
Georg Brandl17e3d692010-07-31 10:08:09 +0000173 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000174 b'501 Syntax: NOOP\r\n')
175
176 def test_QUIT(self):
177 self.write_line(b'QUIT')
Georg Brandl17e3d692010-07-31 10:08:09 +0000178 self.assertEqual(self.channel.socket.last, b'221 Bye\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000179
R David Murray669b7552012-03-20 16:16:29 -0400180 def test_HELO_QUIT(self):
181 self.write_line(b'HELO example')
182 self.write_line(b'QUIT')
183 self.assertEqual(self.channel.socket.last, b'221 Bye\r\n')
184
Richard Jones8cb36192010-07-23 16:20:40 +0000185 def test_QUIT_arg_ignored(self):
186 self.write_line(b'QUIT bye bye')
Georg Brandl17e3d692010-07-31 10:08:09 +0000187 self.assertEqual(self.channel.socket.last, b'221 Bye\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000188
189 def test_bad_state(self):
Georg Brandl17e3d692010-07-31 10:08:09 +0000190 self.channel.smtp_state = 'BAD STATE'
R David Murray669b7552012-03-20 16:16:29 -0400191 self.write_line(b'HELO example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000192 self.assertEqual(self.channel.socket.last,
193 b'451 Internal confusion\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000194
Georg Brandl1e5c5f82010-12-03 07:38:22 +0000195 def test_command_too_long(self):
R David Murray669b7552012-03-20 16:16:29 -0400196 self.write_line(b'HELO example')
R David Murrayd1a30c92012-05-26 14:33:59 -0400197 self.write_line(b'MAIL from: ' +
Georg Brandl1e5c5f82010-12-03 07:38:22 +0000198 b'a' * self.channel.command_size_limit +
199 b'@example')
200 self.assertEqual(self.channel.socket.last,
201 b'500 Error: line too long\r\n')
202
R David Murrayd1a30c92012-05-26 14:33:59 -0400203 def test_MAIL_command_limit_extended_with_SIZE(self):
204 self.write_line(b'EHLO example')
205 fill_len = self.channel.command_size_limit - len('MAIL from:<@example>')
206 self.write_line(b'MAIL from:<' +
207 b'a' * fill_len +
208 b'@example> SIZE=1234')
209 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
210
211 self.write_line(b'MAIL from:<' +
212 b'a' * (fill_len + 26) +
213 b'@example> SIZE=1234')
214 self.assertEqual(self.channel.socket.last,
215 b'500 Error: line too long\r\n')
216
217 def test_data_longer_than_default_data_size_limit(self):
218 # Hack the default so we don't have to generate so much data.
219 self.channel.data_size_limit = 1048
R David Murray669b7552012-03-20 16:16:29 -0400220 self.write_line(b'HELO example')
Georg Brandl1e5c5f82010-12-03 07:38:22 +0000221 self.write_line(b'MAIL From:eggs@example')
222 self.write_line(b'RCPT To:spam@example')
223 self.write_line(b'DATA')
224 self.write_line(b'A' * self.channel.data_size_limit +
225 b'A\r\n.')
226 self.assertEqual(self.channel.socket.last,
227 b'552 Error: Too much mail data\r\n')
228
R David Murrayd1a30c92012-05-26 14:33:59 -0400229 def test_MAIL_size_parameter(self):
230 self.write_line(b'EHLO example')
231 self.write_line(b'MAIL FROM:<eggs@example> SIZE=512')
232 self.assertEqual(self.channel.socket.last,
233 b'250 OK\r\n')
234
235 def test_MAIL_invalid_size_parameter(self):
236 self.write_line(b'EHLO example')
237 self.write_line(b'MAIL FROM:<eggs@example> SIZE=invalid')
238 self.assertEqual(self.channel.socket.last,
239 b'501 Syntax: MAIL FROM: <address> [SP <mail-parameters>]\r\n')
240
241 def test_MAIL_RCPT_unknown_parameters(self):
242 self.write_line(b'EHLO example')
243 self.write_line(b'MAIL FROM:<eggs@example> ham=green')
244 self.assertEqual(self.channel.socket.last,
245 b'555 MAIL FROM parameters not recognized or not implemented\r\n')
246
247 self.write_line(b'MAIL FROM:<eggs@example>')
248 self.write_line(b'RCPT TO:<eggs@example> ham=green')
249 self.assertEqual(self.channel.socket.last,
250 b'555 RCPT TO parameters not recognized or not implemented\r\n')
251
252 def test_MAIL_size_parameter_larger_than_default_data_size_limit(self):
253 self.channel.data_size_limit = 1048
254 self.write_line(b'EHLO example')
255 self.write_line(b'MAIL FROM:<eggs@example> SIZE=2096')
256 self.assertEqual(self.channel.socket.last,
257 b'552 Error: message size exceeds fixed maximum message size\r\n')
258
Richard Jones8cb36192010-07-23 16:20:40 +0000259 def test_need_MAIL(self):
R David Murray669b7552012-03-20 16:16:29 -0400260 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000261 self.write_line(b'RCPT to:spam@example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000262 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000263 b'503 Error: need MAIL command\r\n')
264
R David Murrayd1a30c92012-05-26 14:33:59 -0400265 def test_MAIL_syntax_HELO(self):
R David Murray669b7552012-03-20 16:16:29 -0400266 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000267 self.write_line(b'MAIL from eggs@example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000268 self.assertEqual(self.channel.socket.last,
R David Murrayd1a30c92012-05-26 14:33:59 -0400269 b'501 Syntax: MAIL FROM: <address>\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000270
R David Murrayd1a30c92012-05-26 14:33:59 -0400271 def test_MAIL_syntax_EHLO(self):
272 self.write_line(b'EHLO example')
273 self.write_line(b'MAIL from eggs@example')
274 self.assertEqual(self.channel.socket.last,
275 b'501 Syntax: MAIL FROM: <address> [SP <mail-parameters>]\r\n')
276
277 def test_MAIL_missing_address(self):
R David Murray669b7552012-03-20 16:16:29 -0400278 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000279 self.write_line(b'MAIL from:')
Georg Brandl17e3d692010-07-31 10:08:09 +0000280 self.assertEqual(self.channel.socket.last,
R David Murrayd1a30c92012-05-26 14:33:59 -0400281 b'501 Syntax: MAIL FROM: <address>\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000282
283 def test_MAIL_chevrons(self):
R David Murray669b7552012-03-20 16:16:29 -0400284 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000285 self.write_line(b'MAIL from:<eggs@example>')
R David Murrayd1a30c92012-05-26 14:33:59 -0400286 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
287
288 def test_MAIL_empty_chevrons(self):
289 self.write_line(b'EHLO example')
290 self.write_line(b'MAIL from:<>')
291 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
292
293 def test_MAIL_quoted_localpart(self):
294 self.write_line(b'EHLO example')
295 self.write_line(b'MAIL from: <"Fred Blogs"@example.com>')
296 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
297 self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
298
299 def test_MAIL_quoted_localpart_no_angles(self):
300 self.write_line(b'EHLO example')
301 self.write_line(b'MAIL from: "Fred Blogs"@example.com')
302 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
303 self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
304
305 def test_MAIL_quoted_localpart_with_size(self):
306 self.write_line(b'EHLO example')
307 self.write_line(b'MAIL from: <"Fred Blogs"@example.com> SIZE=1000')
308 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
309 self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
310
311 def test_MAIL_quoted_localpart_with_size_no_angles(self):
312 self.write_line(b'EHLO example')
313 self.write_line(b'MAIL from: "Fred Blogs"@example.com SIZE=1000')
314 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
315 self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
Richard Jones8cb36192010-07-23 16:20:40 +0000316
317 def test_nested_MAIL(self):
R David Murray669b7552012-03-20 16:16:29 -0400318 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000319 self.write_line(b'MAIL from:eggs@example')
320 self.write_line(b'MAIL from:spam@example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000321 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000322 b'503 Error: nested MAIL command\r\n')
323
R David Murrayd1a30c92012-05-26 14:33:59 -0400324 def test_VRFY(self):
325 self.write_line(b'VRFY eggs@example')
326 self.assertEqual(self.channel.socket.last,
327 b'252 Cannot VRFY user, but will accept message and attempt ' + \
328 b'delivery\r\n')
329
330 def test_VRFY_syntax(self):
331 self.write_line(b'VRFY')
332 self.assertEqual(self.channel.socket.last,
333 b'501 Syntax: VRFY <address>\r\n')
334
335 def test_EXPN_not_implemented(self):
336 self.write_line(b'EXPN')
337 self.assertEqual(self.channel.socket.last,
338 b'502 EXPN not implemented\r\n')
339
R David Murray669b7552012-03-20 16:16:29 -0400340 def test_no_HELO_MAIL(self):
341 self.write_line(b'MAIL from:<foo@example.com>')
342 self.assertEqual(self.channel.socket.last,
343 b'503 Error: send HELO first\r\n')
344
Richard Jones8cb36192010-07-23 16:20:40 +0000345 def test_need_RCPT(self):
R David Murray669b7552012-03-20 16:16:29 -0400346 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000347 self.write_line(b'MAIL From:eggs@example')
348 self.write_line(b'DATA')
Georg Brandl17e3d692010-07-31 10:08:09 +0000349 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000350 b'503 Error: need RCPT command\r\n')
351
R David Murrayd1a30c92012-05-26 14:33:59 -0400352 def test_RCPT_syntax_HELO(self):
R David Murray669b7552012-03-20 16:16:29 -0400353 self.write_line(b'HELO example')
R David Murrayd1a30c92012-05-26 14:33:59 -0400354 self.write_line(b'MAIL From: eggs@example')
Richard Jones8cb36192010-07-23 16:20:40 +0000355 self.write_line(b'RCPT to eggs@example')
Georg Brandl17e3d692010-07-31 10:08:09 +0000356 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000357 b'501 Syntax: RCPT TO: <address>\r\n')
358
R David Murrayd1a30c92012-05-26 14:33:59 -0400359 def test_RCPT_syntax_EHLO(self):
360 self.write_line(b'EHLO example')
361 self.write_line(b'MAIL From: eggs@example')
362 self.write_line(b'RCPT to eggs@example')
363 self.assertEqual(self.channel.socket.last,
364 b'501 Syntax: RCPT TO: <address> [SP <mail-parameters>]\r\n')
365
366 def test_RCPT_lowercase_to_OK(self):
367 self.write_line(b'HELO example')
368 self.write_line(b'MAIL From: eggs@example')
369 self.write_line(b'RCPT to: <eggs@example>')
370 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
371
R David Murray669b7552012-03-20 16:16:29 -0400372 def test_no_HELO_RCPT(self):
373 self.write_line(b'RCPT to eggs@example')
374 self.assertEqual(self.channel.socket.last,
375 b'503 Error: send HELO first\r\n')
376
Richard Jones8cb36192010-07-23 16:20:40 +0000377 def test_data_dialog(self):
R David Murray669b7552012-03-20 16:16:29 -0400378 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000379 self.write_line(b'MAIL From:eggs@example')
R David Murrayd1a30c92012-05-26 14:33:59 -0400380 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000381 self.write_line(b'RCPT To:spam@example')
R David Murrayd1a30c92012-05-26 14:33:59 -0400382 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000383
384 self.write_line(b'DATA')
Georg Brandl17e3d692010-07-31 10:08:09 +0000385 self.assertEqual(self.channel.socket.last,
Richard Jones8cb36192010-07-23 16:20:40 +0000386 b'354 End data with <CR><LF>.<CR><LF>\r\n')
387 self.write_line(b'data\r\nmore\r\n.')
R David Murrayd1a30c92012-05-26 14:33:59 -0400388 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000389 self.assertEqual(self.server.messages,
390 [('peer', 'eggs@example', ['spam@example'], 'data\nmore')])
Richard Jones8cb36192010-07-23 16:20:40 +0000391
392 def test_DATA_syntax(self):
R David Murray669b7552012-03-20 16:16:29 -0400393 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000394 self.write_line(b'MAIL From:eggs@example')
395 self.write_line(b'RCPT To:spam@example')
396 self.write_line(b'DATA spam')
Georg Brandl17e3d692010-07-31 10:08:09 +0000397 self.assertEqual(self.channel.socket.last, b'501 Syntax: DATA\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000398
R David Murray669b7552012-03-20 16:16:29 -0400399 def test_no_HELO_DATA(self):
400 self.write_line(b'DATA spam')
401 self.assertEqual(self.channel.socket.last,
402 b'503 Error: send HELO first\r\n')
403
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000404 def test_data_transparency_section_4_5_2(self):
R David Murray669b7552012-03-20 16:16:29 -0400405 self.write_line(b'HELO example')
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000406 self.write_line(b'MAIL From:eggs@example')
407 self.write_line(b'RCPT To:spam@example')
408 self.write_line(b'DATA')
409 self.write_line(b'..\r\n.\r\n')
410 self.assertEqual(self.channel.received_data, '.')
411
Richard Jones8cb36192010-07-23 16:20:40 +0000412 def test_multiple_RCPT(self):
R David Murray669b7552012-03-20 16:16:29 -0400413 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000414 self.write_line(b'MAIL From:eggs@example')
415 self.write_line(b'RCPT To:spam@example')
416 self.write_line(b'RCPT To:ham@example')
417 self.write_line(b'DATA')
418 self.write_line(b'data\r\n.')
Richard Jones6a9e6bb2010-08-04 12:27:36 +0000419 self.assertEqual(self.server.messages,
420 [('peer', 'eggs@example', ['spam@example','ham@example'], 'data')])
Richard Jones8cb36192010-07-23 16:20:40 +0000421
422 def test_manual_status(self):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000423 # checks that the Channel is able to return a custom status message
R David Murray669b7552012-03-20 16:16:29 -0400424 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000425 self.write_line(b'MAIL From:eggs@example')
426 self.write_line(b'RCPT To:spam@example')
427 self.write_line(b'DATA')
428 self.write_line(b'return status\r\n.')
Georg Brandl17e3d692010-07-31 10:08:09 +0000429 self.assertEqual(self.channel.socket.last, b'250 Okish\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000430
431 def test_RSET(self):
R David Murray669b7552012-03-20 16:16:29 -0400432 self.write_line(b'HELO example')
Richard Jones8cb36192010-07-23 16:20:40 +0000433 self.write_line(b'MAIL From:eggs@example')
434 self.write_line(b'RCPT To:spam@example')
435 self.write_line(b'RSET')
R David Murrayd1a30c92012-05-26 14:33:59 -0400436 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000437 self.write_line(b'MAIL From:foo@example')
438 self.write_line(b'RCPT To:eggs@example')
439 self.write_line(b'DATA')
440 self.write_line(b'data\r\n.')
Richard Jones6a9e6bb2010-08-04 12:27:36 +0000441 self.assertEqual(self.server.messages,
442 [('peer', 'foo@example', ['eggs@example'], 'data')])
Richard Jones8cb36192010-07-23 16:20:40 +0000443
R David Murray669b7552012-03-20 16:16:29 -0400444 def test_HELO_RSET(self):
445 self.write_line(b'HELO example')
446 self.write_line(b'RSET')
R David Murrayd1a30c92012-05-26 14:33:59 -0400447 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
R David Murray669b7552012-03-20 16:16:29 -0400448
Richard Jones8cb36192010-07-23 16:20:40 +0000449 def test_RSET_syntax(self):
450 self.write_line(b'RSET hi')
Georg Brandl17e3d692010-07-31 10:08:09 +0000451 self.assertEqual(self.channel.socket.last, b'501 Syntax: RSET\r\n')
Richard Jones8cb36192010-07-23 16:20:40 +0000452
R David Murrayd1a30c92012-05-26 14:33:59 -0400453 def test_unknown_command(self):
454 self.write_line(b'UNKNOWN_CMD')
455 self.assertEqual(self.channel.socket.last,
456 b'500 Error: command "UNKNOWN_CMD" not ' + \
457 b'recognized\r\n')
458
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000459 def test_attribute_deprecations(self):
Florent Xicluna67317752011-12-10 11:07:42 +0100460 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000461 spam = self.channel._SMTPChannel__server
Florent Xicluna67317752011-12-10 11:07:42 +0100462 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000463 self.channel._SMTPChannel__server = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100464 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000465 spam = self.channel._SMTPChannel__line
Florent Xicluna67317752011-12-10 11:07:42 +0100466 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000467 self.channel._SMTPChannel__line = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100468 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000469 spam = self.channel._SMTPChannel__state
Florent Xicluna67317752011-12-10 11:07:42 +0100470 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000471 self.channel._SMTPChannel__state = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100472 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000473 spam = self.channel._SMTPChannel__greeting
Florent Xicluna67317752011-12-10 11:07:42 +0100474 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000475 self.channel._SMTPChannel__greeting = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100476 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000477 spam = self.channel._SMTPChannel__mailfrom
Florent Xicluna67317752011-12-10 11:07:42 +0100478 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000479 self.channel._SMTPChannel__mailfrom = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100480 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000481 spam = self.channel._SMTPChannel__rcpttos
Florent Xicluna67317752011-12-10 11:07:42 +0100482 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000483 self.channel._SMTPChannel__rcpttos = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100484 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000485 spam = self.channel._SMTPChannel__data
Florent Xicluna67317752011-12-10 11:07:42 +0100486 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000487 self.channel._SMTPChannel__data = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100488 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000489 spam = self.channel._SMTPChannel__fqdn
Florent Xicluna67317752011-12-10 11:07:42 +0100490 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000491 self.channel._SMTPChannel__fqdn = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100492 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000493 spam = self.channel._SMTPChannel__peer
Florent Xicluna67317752011-12-10 11:07:42 +0100494 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000495 self.channel._SMTPChannel__peer = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100496 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000497 spam = self.channel._SMTPChannel__conn
Florent Xicluna67317752011-12-10 11:07:42 +0100498 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000499 self.channel._SMTPChannel__conn = 'spam'
Florent Xicluna67317752011-12-10 11:07:42 +0100500 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000501 spam = self.channel._SMTPChannel__addr
Florent Xicluna67317752011-12-10 11:07:42 +0100502 with support.check_warnings(('', DeprecationWarning)):
Richard Jones4aa0d4d2010-08-04 01:20:14 +0000503 self.channel._SMTPChannel__addr = 'spam'
Richard Jones8cb36192010-07-23 16:20:40 +0000504
R David Murrayd1a30c92012-05-26 14:33:59 -0400505
506class SMTPDChannelWithDataSizeLimitTest(unittest.TestCase):
507
508 def setUp(self):
509 smtpd.socket = asyncore.socket = mock_socket
R David Murray05cab752012-06-04 15:55:51 -0400510 self.old_debugstream = smtpd.DEBUGSTREAM
R David Murrayd1a30c92012-05-26 14:33:59 -0400511 self.debug = smtpd.DEBUGSTREAM = io.StringIO()
512 self.server = DummyServer('a', 'b')
513 conn, addr = self.server.accept()
514 # Set DATA size limit to 32 bytes for easy testing
515 self.channel = smtpd.SMTPChannel(self.server, conn, addr, 32)
516
517 def tearDown(self):
518 asyncore.close_all()
519 asyncore.socket = smtpd.socket = socket
R David Murray05cab752012-06-04 15:55:51 -0400520 smtpd.DEBUGSTREAM = self.old_debugstream
R David Murrayd1a30c92012-05-26 14:33:59 -0400521
522 def write_line(self, line):
523 self.channel.socket.queue_recv(line)
524 self.channel.handle_read()
525
526 def test_data_limit_dialog(self):
527 self.write_line(b'HELO example')
528 self.write_line(b'MAIL From:eggs@example')
529 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
530 self.write_line(b'RCPT To:spam@example')
531 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
532
533 self.write_line(b'DATA')
534 self.assertEqual(self.channel.socket.last,
535 b'354 End data with <CR><LF>.<CR><LF>\r\n')
536 self.write_line(b'data\r\nmore\r\n.')
537 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
538 self.assertEqual(self.server.messages,
539 [('peer', 'eggs@example', ['spam@example'], 'data\nmore')])
540
541 def test_data_limit_dialog_too_much_data(self):
542 self.write_line(b'HELO example')
543 self.write_line(b'MAIL From:eggs@example')
544 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
545 self.write_line(b'RCPT To:spam@example')
546 self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
547
548 self.write_line(b'DATA')
549 self.assertEqual(self.channel.socket.last,
550 b'354 End data with <CR><LF>.<CR><LF>\r\n')
551 self.write_line(b'This message is longer than 32 bytes\r\n.')
552 self.assertEqual(self.channel.socket.last,
553 b'552 Error: Too much mail data\r\n')
554
Richard Jones8cb36192010-07-23 16:20:40 +0000555
556if __name__ == "__main__":
R David Murrayd1a30c92012-05-26 14:33:59 -0400557 unittest.main()