| LEFT | RIGHT |
| 1 from unittest import TestCase | 1 import unittest |
| 2 from test import support, mock_socket | 2 from test import support, mock_socket |
| 3 import socket | 3 import socket |
| 4 import io | 4 import io |
| 5 import smtpd | 5 import smtpd |
| 6 import asyncore | 6 import asyncore |
| 7 | 7 |
| 8 | 8 |
| 9 class DummyServer(smtpd.SMTPServer): | 9 class DummyServer(smtpd.SMTPServer): |
| 10 def __init__(self, localaddr, remoteaddr): | 10 def __init__(self, localaddr, remoteaddr): |
| 11 smtpd.SMTPServer.__init__(self, localaddr, remoteaddr) | 11 smtpd.SMTPServer.__init__(self, localaddr, remoteaddr) |
| 12 self.messages = [] | 12 self.messages = [] |
| 13 | 13 |
| 14 def process_message(self, peer, mailfrom, rcpttos, data): | 14 def process_message(self, peer, mailfrom, rcpttos, data): |
| 15 self.messages.append((peer, mailfrom, rcpttos, data)) | 15 self.messages.append((peer, mailfrom, rcpttos, data)) |
| 16 if data == 'return status': | 16 if data == 'return status': |
| 17 return '250 Okish' | 17 return '250 Okish' |
| 18 | 18 |
| 19 | 19 |
| 20 class DummyDispatcherBroken(Exception): | 20 class DummyDispatcherBroken(Exception): |
| 21 pass | 21 pass |
| 22 | 22 |
| 23 | 23 |
| 24 class BrokenDummyServer(DummyServer): | 24 class BrokenDummyServer(DummyServer): |
| 25 def listen(self, num): | 25 def listen(self, num): |
| 26 raise DummyDispatcherBroken() | 26 raise DummyDispatcherBroken() |
| 27 | 27 |
| 28 | 28 |
| 29 class SMTPDServerTest(TestCase): | 29 class SMTPDServerTest(unittest.TestCase): |
| 30 def setUp(self): | 30 def setUp(self): |
| 31 smtpd.socket = asyncore.socket = mock_socket | 31 smtpd.socket = asyncore.socket = mock_socket |
| 32 | 32 |
| 33 def test_process_message_unimplemented(self): | 33 def test_process_message_unimplemented(self): |
| 34 server = smtpd.SMTPServer('a', 'b') | 34 server = smtpd.SMTPServer('a', 'b') |
| 35 conn, addr = server.accept() | 35 conn, addr = server.accept() |
| 36 channel = smtpd.SMTPChannel(server, conn, addr) | 36 channel = smtpd.SMTPChannel(server, conn, addr) |
| 37 | 37 |
| 38 def write_line(line): | 38 def write_line(line): |
| 39 channel.socket.queue_recv(line) | 39 channel.socket.queue_recv(line) |
| 40 channel.handle_read() | 40 channel.handle_read() |
| 41 | 41 |
| 42 write_line(b'MAIL From:eggs@example') | 42 write_line(b'MAIL From:eggs@example') |
| 43 write_line(b'RCPT To:spam@example') | 43 write_line(b'RCPT To:spam@example') |
| 44 write_line(b'DATA') | 44 write_line(b'DATA') |
| 45 self.assertRaises(NotImplementedError, write_line, b'spam\r\n.\r\n') | 45 self.assertRaises(NotImplementedError, write_line, b'spam\r\n.\r\n') |
| 46 | 46 |
| 47 def tearDown(self): | 47 def tearDown(self): |
| 48 asyncore.close_all() | 48 asyncore.close_all() |
| 49 asyncore.socket = smtpd.socket = socket | 49 asyncore.socket = smtpd.socket = socket |
| 50 | 50 |
| 51 | 51 |
| 52 class SMTPDChannelTest(TestCase): | 52 class SMTPDChannelTest(unittest.TestCase): |
| 53 def setUp(self): | 53 def setUp(self): |
| 54 smtpd.socket = asyncore.socket = mock_socket | 54 smtpd.socket = asyncore.socket = mock_socket |
| 55 self.old_debugstream = smtpd.DEBUGSTREAM |
| 55 self.debug = smtpd.DEBUGSTREAM = io.StringIO() | 56 self.debug = smtpd.DEBUGSTREAM = io.StringIO() |
| 56 self.server = DummyServer('a', 'b') | 57 self.server = DummyServer('a', 'b') |
| 57 conn, addr = self.server.accept() | 58 conn, addr = self.server.accept() |
| 58 self.channel = smtpd.SMTPChannel(self.server, conn, addr) | 59 self.channel = smtpd.SMTPChannel(self.server, conn, addr) |
| 59 | 60 |
| 60 def tearDown(self): | 61 def tearDown(self): |
| 61 asyncore.close_all() | 62 asyncore.close_all() |
| 62 asyncore.socket = smtpd.socket = socket | 63 asyncore.socket = smtpd.socket = socket |
| 64 smtpd.DEBUGSTREAM = self.old_debugstream |
| 63 | 65 |
| 64 def write_line(self, line): | 66 def write_line(self, line): |
| 65 self.channel.socket.queue_recv(line) | 67 self.channel.socket.queue_recv(line) |
| 66 self.channel.handle_read() | 68 self.channel.handle_read() |
| 67 | 69 |
| 68 def test_broken_connect(self): | 70 def test_broken_connect(self): |
| 69 self.assertRaises(DummyDispatcherBroken, BrokenDummyServer, 'a', 'b') | 71 self.assertRaises(DummyDispatcherBroken, BrokenDummyServer, 'a', 'b') |
| 70 | 72 |
| 71 def test_server_accept(self): | 73 def test_server_accept(self): |
| 72 self.server.handle_accept() | 74 self.server.handle_accept() |
| 73 | 75 |
| 74 def test_missing_data(self): | 76 def test_missing_data(self): |
| 75 self.write_line(b'') | 77 self.write_line(b'') |
| 76 self.assertEqual(self.channel.socket.last, | 78 self.assertEqual(self.channel.socket.last, |
| 77 b'500 Error: bad syntax\r\n') | 79 b'500 Error: bad syntax\r\n') |
| 78 | 80 |
| 79 def test_EHLO(self): | 81 def test_EHLO(self): |
| 80 self.write_line(b'EHLO test.example') | 82 self.write_line(b'EHLO test.example') |
| 81 self.assertEqual(self.channel.socket.last, b'250 HELP\r\n') | 83 self.assertEqual(self.channel.socket.last, b'250 HELP\r\n') |
| 82 | 84 |
| 83 def test_EHLO_bad_syntax(self): | 85 def test_EHLO_bad_syntax(self): |
| 84 self.write_line(b'EHLO') | 86 self.write_line(b'EHLO') |
| 85 self.assertEqual(self.channel.socket.last, | 87 self.assertEqual(self.channel.socket.last, |
| 86 b'501 Syntax: EHLO hostname\r\n') | 88 b'501 Syntax: EHLO hostname\r\n') |
| 87 | 89 |
| 88 def test_EHLO_duplicate(self): | 90 def test_EHLO_duplicate(self): |
| 89 self.write_line(b'EHLO test.example') | 91 self.write_line(b'EHLO test.example') |
| 90 self.write_line(b'EHLO test.example') | 92 self.write_line(b'EHLO test.example') |
| 91 self.assertEqual(self.channel.socket.last, | 93 self.assertEqual(self.channel.socket.last, |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 130 self.write_line(b'HELO') | 132 self.write_line(b'HELO') |
| 131 self.assertEqual(self.channel.socket.last, | 133 self.assertEqual(self.channel.socket.last, |
| 132 b'501 Syntax: HELO hostname\r\n') | 134 b'501 Syntax: HELO hostname\r\n') |
| 133 | 135 |
| 134 def test_HELO_duplicate(self): | 136 def test_HELO_duplicate(self): |
| 135 self.write_line(b'HELO test.example') | 137 self.write_line(b'HELO test.example') |
| 136 self.write_line(b'HELO test.example') | 138 self.write_line(b'HELO test.example') |
| 137 self.assertEqual(self.channel.socket.last, | 139 self.assertEqual(self.channel.socket.last, |
| 138 b'503 Duplicate HELO/EHLO\r\n') | 140 b'503 Duplicate HELO/EHLO\r\n') |
| 139 | 141 |
| 142 def test_HELO_extensions_not_enabled(self): |
| 143 self.extended_smtp = False |
| 144 self.write_line(b'HELO test.example') |
| 145 self.write_line(b'MAIL from:<foo@example.com> SIZE=1234') |
| 146 self.assertEqual(self.channel.socket.last, |
| 147 b'501 Syntax: MAIL FROM:<address>\r\n') |
| 148 |
| 149 def test_no_HELO_EHLO_defaults_to_HELO(self): |
| 150 self.write_line(b'MAIL FROM:<eggs@example> SIZE=10') |
| 151 self.assertEqual(self.channel.socket.last, |
| 152 b'501 Syntax: MAIL FROM:<address>\r\n') |
| 153 |
| 154 def test_MAIL_allows_space_after_colon(self): |
| 155 self.write_line(b'HELO test.example') |
| 156 self.write_line(b'MAIL from: <foo@example.com>') |
| 157 self.assertEqual(self.channel.socket.last, |
| 158 b'250 OK\r\n') |
| 159 |
| 160 def test_extended_MAIL_allows_space_after_colon(self): |
| 161 self.write_line(b'EHLO test.example') |
| 162 self.write_line(b'MAIL from: <foo@example.com> size=20') |
| 163 self.assertEqual(self.channel.socket.last, |
| 164 b'250 OK\r\n') |
| 165 |
| 140 def test_NOOP(self): | 166 def test_NOOP(self): |
| 141 self.write_line(b'NOOP') | 167 self.write_line(b'NOOP') |
| 142 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') | 168 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') |
| 143 | 169 |
| 144 def test_NOOP_bad_syntax(self): | 170 def test_NOOP_bad_syntax(self): |
| 145 self.write_line(b'NOOP hi') | 171 self.write_line(b'NOOP hi') |
| 146 self.assertEqual(self.channel.socket.last, | 172 self.assertEqual(self.channel.socket.last, |
| 147 b'501 Syntax: NOOP\r\n') | 173 b'501 Syntax: NOOP\r\n') |
| 148 | |
| 149 def test_8BITMIME(self): | |
| 150 self.write_line(b'8BITMIME') | |
| 151 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') | |
| 152 | |
| 153 def test_8BITMIME_syntax(self): | |
| 154 self.write_line(b'8BITMIME ON') | |
| 155 self.assertEqual(self.channel.socket.last, b'501 Syntax: 8BITMIME\r\n') | |
| 156 | 174 |
| 157 def test_QUIT(self): | 175 def test_QUIT(self): |
| 158 self.write_line(b'QUIT') | 176 self.write_line(b'QUIT') |
| 159 self.assertEqual(self.channel.socket.last, b'221 Bye\r\n') | 177 self.assertEqual(self.channel.socket.last, b'221 Bye\r\n') |
| 160 | 178 |
| 161 def test_QUIT_arg_ignored(self): | 179 def test_QUIT_arg_ignored(self): |
| 162 self.write_line(b'QUIT bye bye') | 180 self.write_line(b'QUIT bye bye') |
| 163 self.assertEqual(self.channel.socket.last, b'221 Bye\r\n') | 181 self.assertEqual(self.channel.socket.last, b'221 Bye\r\n') |
| 164 | 182 |
| 165 def test_bad_state(self): | 183 def test_bad_state(self): |
| 166 self.channel.smtp_state = 'BAD STATE' | 184 self.channel.smtp_state = 'BAD STATE' |
| 167 self.write_line(b'HELO') | 185 self.write_line(b'HELO') |
| 168 self.assertEqual(self.channel.socket.last, | 186 self.assertEqual(self.channel.socket.last, |
| 169 b'451 Internal confusion\r\n') | 187 b'451 Internal confusion\r\n') |
| 170 | 188 |
| 189 def test_command_too_long(self): |
| 190 self.write_line(b'HELO test.example') |
| 191 self.write_line(b'MAIL from:<' + |
| 192 b'a' * self.channel.command_size_limit + |
| 193 b'@example>') |
| 194 self.assertEqual(self.channel.socket.last, |
| 195 b'500 Error: line too long\r\n') |
| 196 |
| 197 def test_MAIL_command_limit_extended_with_SIZE(self): |
| 198 self.write_line(b'EHLO test.example') |
| 199 fill_len = self.channel.command_size_limit - len('MAIL from:<@example>') |
| 200 self.write_line(b'MAIL from:<' + |
| 201 b'a' * fill_len + |
| 202 b'@example> SIZE=1234') |
| 203 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') |
| 204 |
| 205 self.write_line(b'MAIL from:<' + |
| 206 b'a' * (fill_len + 26) + |
| 207 b'@example> SIZE=1234') |
| 208 self.assertEqual(self.channel.socket.last, |
| 209 b'500 Error: line too long\r\n') |
| 210 |
| 211 def test_data_longer_than_default_data_size_limit(self): |
| 212 # Hack the default so we don't have to generate so much data. |
| 213 self.channel.data_size_limit = 1048 |
| 214 self.write_line(b'MAIL From:eggs@example') |
| 215 self.write_line(b'RCPT To:spam@example') |
| 216 self.write_line(b'DATA') |
| 217 self.write_line(b'A' * self.channel.data_size_limit + |
| 218 b'A\r\n.') |
| 219 self.assertEqual(self.channel.socket.last, |
| 220 b'552 Error: Too much mail data\r\n') |
| 221 |
| 222 def test_MAIL_ok_without_HELO_EHLO(self): |
| 223 # This actually violates the RFC, but we haven't historically |
| 224 # enforced this, and old mail servers didn't either. |
| 225 self.write_line(b'MAIL FROM:<eggs@example>') |
| 226 self.assertEqual(self.channel.socket.last, |
| 227 b'250 OK\r\n') |
| 228 |
| 229 def test_MAIL_size_parameter(self): |
| 230 self.write_line(b'EHLO test.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 test.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 test.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 test.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 |
| 171 def test_need_MAIL(self): | 259 def test_need_MAIL(self): |
| 172 self.write_line(b'RCPT to:spam@example') | 260 self.write_line(b'RCPT to:spam@example') |
| 173 self.assertEqual(self.channel.socket.last, | 261 self.assertEqual(self.channel.socket.last, |
| 174 b'503 Error: need MAIL command\r\n') | 262 b'503 Error: need MAIL command\r\n') |
| 175 | 263 |
| 176 def test_MAIL_syntax(self): | 264 def test_MAIL_syntax(self): |
| 265 self.write_line(b'EHLO test.example') |
| 177 self.write_line(b'MAIL from eggs@example') | 266 self.write_line(b'MAIL from eggs@example') |
| 178 self.assertEqual(self.channel.socket.last, | 267 self.assertEqual(self.channel.socket.last, |
| 179 b'501 Syntax: MAIL FROM:<address>\r\n') | 268 b'501 Syntax: MAIL FROM:<address> [SP <mail-parameters>]\r\n') |
| 180 | 269 |
| 181 def test_MAIL_missing_from(self): | 270 def test_MAIL_missing_from(self): |
| 271 self.write_line(b'EHLO test.example') |
| 182 self.write_line(b'MAIL from:') | 272 self.write_line(b'MAIL from:') |
| 183 self.assertEqual(self.channel.socket.last, | 273 self.assertEqual(self.channel.socket.last, |
| 184 b'501 Syntax: MAIL FROM:<address>\r\n') | 274 b'501 Syntax: MAIL FROM:<address> [SP <mail-parameters>]\r\n') |
| 185 | 275 |
| 186 def test_MAIL_chevrons(self): | 276 def test_MAIL_chevrons(self): |
| 277 self.write_line(b'EHLO test.example') |
| 187 self.write_line(b'MAIL from:<eggs@example>') | 278 self.write_line(b'MAIL from:<eggs@example>') |
| 188 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') | 279 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') |
| 189 | 280 |
| 281 def test_MAIL_empty_chevrons(self): |
| 282 self.write_line(b'EHLO test.example') |
| 283 self.write_line(b'MAIL from:<>') |
| 284 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') |
| 285 |
| 286 # Parsing of quoted localpart of email addresses requires a full parser |
| 287 # implementation, and will need to wait for email 6 package. See RFC 3696 |
| 288 # for valid addresses the server needs to be able to parse. |
| 289 @unittest.expectedFailure |
| 290 def test_MAIL_quoted_localpart(self): |
| 291 self.write_line(b'EHLO test.example') |
| 292 self.write_line(b'MAIL from:<"Fred Blogs"@example.com>') |
| 293 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') |
| 294 self.write_line(b'RSET') |
| 295 self.write_line(b'MAIL from:<Abc\>def@example.com>') |
| 296 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') |
| 297 |
| 190 def test_nested_MAIL(self): | 298 def test_nested_MAIL(self): |
| 299 self.write_line(b'EHLO test.example') |
| 191 self.write_line(b'MAIL from:eggs@example') | 300 self.write_line(b'MAIL from:eggs@example') |
| 192 self.write_line(b'MAIL from:spam@example') | 301 self.write_line(b'MAIL from:spam@example') |
| 193 self.assertEqual(self.channel.socket.last, | 302 self.assertEqual(self.channel.socket.last, |
| 194 b'503 Error: nested MAIL command\r\n') | 303 b'503 Error: nested MAIL command\r\n') |
| 195 | 304 |
| 196 def test_VRFY(self): | 305 def test_VRFY(self): |
| 197 self.write_line(b'VRFY eggs@example') | 306 self.write_line(b'VRFY eggs@example') |
| 198 self.assertEqual(self.channel.socket.last, | 307 self.assertEqual(self.channel.socket.last, |
| 199 b'252 Cannot VRFY user, but will accept message and attempt ' + \ | 308 b'252 Cannot VRFY user, but will accept message and attempt ' + \ |
| 200 b'delivery\r\n') | 309 b'delivery\r\n') |
| 201 | 310 |
| 202 def test_VRFY_syntax(self): | 311 def test_VRFY_syntax(self): |
| 203 self.write_line(b'VRFY') | 312 self.write_line(b'VRFY') |
| 204 self.assertEqual(self.channel.socket.last, | 313 self.assertEqual(self.channel.socket.last, |
| 205 b'501 Syntax: VRFY <address>\r\n') | 314 b'501 Syntax: VRFY <address>\r\n') |
| 206 | 315 |
| 207 def test_EXPN_not_implemented(self): | 316 def test_EXPN_not_implemented(self): |
| 208 self.write_line(b'EXPN') | 317 self.write_line(b'EXPN') |
| 209 self.assertEqual(self.channel.socket.last, | 318 self.assertEqual(self.channel.socket.last, |
| 210 b'502 EXPN not implemented\r\n') | 319 b'502 EXPN not implemented\r\n') |
| 211 | 320 |
| 212 def test_need_RCPT(self): | 321 def test_need_RCPT(self): |
| 322 self.write_line(b'EHLO test.example') |
| 213 self.write_line(b'MAIL From:eggs@example') | 323 self.write_line(b'MAIL From:eggs@example') |
| 214 self.write_line(b'DATA') | 324 self.write_line(b'DATA') |
| 215 self.assertEqual(self.channel.socket.last, | 325 self.assertEqual(self.channel.socket.last, |
| 216 b'503 Error: need RCPT command\r\n') | 326 b'503 Error: need RCPT command\r\n') |
| 217 | 327 |
| 218 def test_RCPT_syntax(self): | 328 def test_RCPT_syntax(self): |
| 329 self.write_line(b'EHLO test.example') |
| 219 self.write_line(b'MAIL From:eggs@example') | 330 self.write_line(b'MAIL From:eggs@example') |
| 220 self.write_line(b'RCPT to eggs@example') | 331 self.write_line(b'RCPT to eggs@example') |
| 221 self.assertEqual(self.channel.socket.last, | 332 self.assertEqual(self.channel.socket.last, |
| 222 b'501 Syntax: RCPT TO: <address>\r\n') | 333 b'501 Syntax: RCPT TO:<address> [SP <mail-parameters>]\r\n') |
| 334 |
| 335 self.write_line(b'RCPT to:<eggs@example>') |
| 336 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') |
| 223 | 337 |
| 224 def test_data_dialog(self): | 338 def test_data_dialog(self): |
| 339 self.write_line(b'EHLO test.example') |
| 225 self.write_line(b'MAIL From:eggs@example') | 340 self.write_line(b'MAIL From:eggs@example') |
| 226 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') | 341 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') |
| 227 self.write_line(b'RCPT To:spam@example') | 342 self.write_line(b'RCPT To:spam@example') |
| 228 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') | 343 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') |
| 229 | 344 |
| 230 self.write_line(b'DATA') | 345 self.write_line(b'DATA') |
| 231 self.assertEqual(self.channel.socket.last, | 346 self.assertEqual(self.channel.socket.last, |
| 232 b'354 End data with <CR><LF>.<CR><LF>\r\n') | 347 b'354 End data with <CR><LF>.<CR><LF>\r\n') |
| 233 self.write_line(b'data\r\nmore\r\n.') | 348 self.write_line(b'data\r\nmore\r\n.') |
| 234 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') | 349 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') |
| 235 self.assertEqual(self.server.messages, | 350 self.assertEqual(self.server.messages, |
| 236 [('peer', 'eggs@example', ['spam@example'], 'data\nmore')]) | 351 [('peer', 'eggs@example', ['spam@example'], 'data\nmore')]) |
| 237 | 352 |
| 238 def test_DATA_syntax(self): | 353 def test_DATA_syntax(self): |
| 354 self.write_line(b'EHLO test.example') |
| 239 self.write_line(b'MAIL From:eggs@example') | 355 self.write_line(b'MAIL From:eggs@example') |
| 240 self.write_line(b'RCPT To:spam@example') | 356 self.write_line(b'RCPT To:spam@example') |
| 241 self.write_line(b'DATA spam') | 357 self.write_line(b'DATA spam') |
| 242 self.assertEqual(self.channel.socket.last, b'501 Syntax: DATA\r\n') | 358 self.assertEqual(self.channel.socket.last, b'501 Syntax: DATA\r\n') |
| 243 | 359 |
| 244 def test_data_transparency_section_4_5_2(self): | 360 def test_data_transparency_section_4_5_2(self): |
| 361 self.write_line(b'EHLO test.example') |
| 245 self.write_line(b'MAIL From:eggs@example') | 362 self.write_line(b'MAIL From:eggs@example') |
| 246 self.write_line(b'RCPT To:spam@example') | 363 self.write_line(b'RCPT To:spam@example') |
| 247 self.write_line(b'DATA') | 364 self.write_line(b'DATA') |
| 248 self.write_line(b'..\r\n.\r\n') | 365 self.write_line(b'..\r\n.\r\n') |
| 249 self.assertEqual(self.channel.received_data, '.') | 366 self.assertEqual(self.channel.received_data, '.') |
| 250 | 367 |
| 251 def test_multiple_RCPT(self): | 368 def test_multiple_RCPT(self): |
| 369 self.write_line(b'EHLO test.example') |
| 252 self.write_line(b'MAIL From:eggs@example') | 370 self.write_line(b'MAIL From:eggs@example') |
| 253 self.write_line(b'RCPT To:spam@example') | 371 self.write_line(b'RCPT To:spam@example') |
| 254 self.write_line(b'RCPT To:ham@example') | 372 self.write_line(b'RCPT To:ham@example') |
| 255 self.write_line(b'DATA') | 373 self.write_line(b'DATA') |
| 256 self.write_line(b'data\r\n.') | 374 self.write_line(b'data\r\n.') |
| 257 self.assertEqual(self.server.messages, | 375 self.assertEqual(self.server.messages, |
| 258 [('peer', 'eggs@example', ['spam@example','ham@example'], 'data')]) | 376 [('peer', 'eggs@example', ['spam@example','ham@example'], 'data')]) |
| 259 | 377 |
| 260 def test_manual_status(self): | 378 def test_manual_status(self): |
| 261 # checks that the Channel is able to return a custom status message | 379 # checks that the Channel is able to return a custom status message |
| 380 self.write_line(b'EHLO test.example') |
| 262 self.write_line(b'MAIL From:eggs@example') | 381 self.write_line(b'MAIL From:eggs@example') |
| 263 self.write_line(b'RCPT To:spam@example') | 382 self.write_line(b'RCPT To:spam@example') |
| 264 self.write_line(b'DATA') | 383 self.write_line(b'DATA') |
| 265 self.write_line(b'return status\r\n.') | 384 self.write_line(b'return status\r\n.') |
| 266 self.assertEqual(self.channel.socket.last, b'250 Okish\r\n') | 385 self.assertEqual(self.channel.socket.last, b'250 Okish\r\n') |
| 267 | 386 |
| 268 def test_RSET(self): | 387 def test_RSET(self): |
| 388 self.write_line(b'EHLO test.example') |
| 269 self.write_line(b'MAIL From:eggs@example') | 389 self.write_line(b'MAIL From:eggs@example') |
| 270 self.write_line(b'RCPT To:spam@example') | 390 self.write_line(b'RCPT To:spam@example') |
| 271 self.write_line(b'RSET') | 391 self.write_line(b'RSET') |
| 272 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') | 392 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') |
| 273 self.write_line(b'MAIL From:foo@example') | 393 self.write_line(b'MAIL From:foo@example') |
| 274 self.write_line(b'RCPT To:eggs@example') | 394 self.write_line(b'RCPT To:eggs@example') |
| 275 self.write_line(b'DATA') | 395 self.write_line(b'DATA') |
| 276 self.write_line(b'data\r\n.') | 396 self.write_line(b'data\r\n.') |
| 277 self.assertEqual(self.server.messages, | 397 self.assertEqual(self.server.messages, |
| 278 [('peer', 'foo@example', ['eggs@example'], 'data')]) | 398 [('peer', 'foo@example', ['eggs@example'], 'data')]) |
| 279 | 399 |
| 280 def test_RSET_syntax(self): | 400 def test_RSET_syntax(self): |
| 281 self.write_line(b'RSET hi') | 401 self.write_line(b'RSET hi') |
| 282 self.assertEqual(self.channel.socket.last, b'501 Syntax: RSET\r\n') | 402 self.assertEqual(self.channel.socket.last, b'501 Syntax: RSET\r\n') |
| 283 | 403 |
| 284 def test_unknown_command(self): | 404 def test_unknown_command(self): |
| 285 self.write_line(b'UNKNOWN_CMD') | 405 self.write_line(b'UNKNOWN_CMD') |
| 286 self.assertEqual(self.channel.socket.last, | 406 self.assertEqual(self.channel.socket.last, |
| 287 b'500 Error: command "UNKNOWN_CMD" not ' + \ | 407 b'500 Error: command "UNKNOWN_CMD" not ' + \ |
| 288 b'recognized\r\n') | 408 b'recognized\r\n') |
| 289 | 409 |
| 290 def test_attribute_deprecations(self): | 410 def test_attribute_deprecations(self): |
| 291 with support.check_warnings(('', PendingDeprecationWarning)): | 411 with support.check_warnings(('', DeprecationWarning)): |
| 292 spam = self.channel._SMTPChannel__server | 412 spam = self.channel._SMTPChannel__server |
| 293 with support.check_warnings(('', PendingDeprecationWarning)): | 413 with support.check_warnings(('', DeprecationWarning)): |
| 294 self.channel._SMTPChannel__server = 'spam' | 414 self.channel._SMTPChannel__server = 'spam' |
| 295 with support.check_warnings(('', PendingDeprecationWarning)): | 415 with support.check_warnings(('', DeprecationWarning)): |
| 296 spam = self.channel._SMTPChannel__line | 416 spam = self.channel._SMTPChannel__line |
| 297 with support.check_warnings(('', PendingDeprecationWarning)): | 417 with support.check_warnings(('', DeprecationWarning)): |
| 298 self.channel._SMTPChannel__line = 'spam' | 418 self.channel._SMTPChannel__line = 'spam' |
| 299 with support.check_warnings(('', PendingDeprecationWarning)): | 419 with support.check_warnings(('', DeprecationWarning)): |
| 300 spam = self.channel._SMTPChannel__state | 420 spam = self.channel._SMTPChannel__state |
| 301 with support.check_warnings(('', PendingDeprecationWarning)): | 421 with support.check_warnings(('', DeprecationWarning)): |
| 302 self.channel._SMTPChannel__state = 'spam' | 422 self.channel._SMTPChannel__state = 'spam' |
| 303 with support.check_warnings(('', PendingDeprecationWarning)): | 423 with support.check_warnings(('', DeprecationWarning)): |
| 304 spam = self.channel._SMTPChannel__greeting | 424 spam = self.channel._SMTPChannel__greeting |
| 305 with support.check_warnings(('', PendingDeprecationWarning)): | 425 with support.check_warnings(('', DeprecationWarning)): |
| 306 self.channel._SMTPChannel__greeting = 'spam' | 426 self.channel._SMTPChannel__greeting = 'spam' |
| 307 with support.check_warnings(('', PendingDeprecationWarning)): | 427 with support.check_warnings(('', DeprecationWarning)): |
| 308 spam = self.channel._SMTPChannel__mailfrom | 428 spam = self.channel._SMTPChannel__mailfrom |
| 309 with support.check_warnings(('', PendingDeprecationWarning)): | 429 with support.check_warnings(('', DeprecationWarning)): |
| 310 self.channel._SMTPChannel__mailfrom = 'spam' | 430 self.channel._SMTPChannel__mailfrom = 'spam' |
| 311 with support.check_warnings(('', PendingDeprecationWarning)): | 431 with support.check_warnings(('', DeprecationWarning)): |
| 312 spam = self.channel._SMTPChannel__rcpttos | 432 spam = self.channel._SMTPChannel__rcpttos |
| 313 with support.check_warnings(('', PendingDeprecationWarning)): | 433 with support.check_warnings(('', DeprecationWarning)): |
| 314 self.channel._SMTPChannel__rcpttos = 'spam' | 434 self.channel._SMTPChannel__rcpttos = 'spam' |
| 315 with support.check_warnings(('', PendingDeprecationWarning)): | 435 with support.check_warnings(('', DeprecationWarning)): |
| 316 spam = self.channel._SMTPChannel__data | 436 spam = self.channel._SMTPChannel__data |
| 317 with support.check_warnings(('', PendingDeprecationWarning)): | 437 with support.check_warnings(('', DeprecationWarning)): |
| 318 self.channel._SMTPChannel__data = 'spam' | 438 self.channel._SMTPChannel__data = 'spam' |
| 319 with support.check_warnings(('', PendingDeprecationWarning)): | 439 with support.check_warnings(('', DeprecationWarning)): |
| 320 spam = self.channel._SMTPChannel__fqdn | 440 spam = self.channel._SMTPChannel__fqdn |
| 321 with support.check_warnings(('', PendingDeprecationWarning)): | 441 with support.check_warnings(('', DeprecationWarning)): |
| 322 self.channel._SMTPChannel__fqdn = 'spam' | 442 self.channel._SMTPChannel__fqdn = 'spam' |
| 323 with support.check_warnings(('', PendingDeprecationWarning)): | 443 with support.check_warnings(('', DeprecationWarning)): |
| 324 spam = self.channel._SMTPChannel__peer | 444 spam = self.channel._SMTPChannel__peer |
| 325 with support.check_warnings(('', PendingDeprecationWarning)): | 445 with support.check_warnings(('', DeprecationWarning)): |
| 326 self.channel._SMTPChannel__peer = 'spam' | 446 self.channel._SMTPChannel__peer = 'spam' |
| 327 with support.check_warnings(('', PendingDeprecationWarning)): | 447 with support.check_warnings(('', DeprecationWarning)): |
| 328 spam = self.channel._SMTPChannel__conn | 448 spam = self.channel._SMTPChannel__conn |
| 329 with support.check_warnings(('', PendingDeprecationWarning)): | 449 with support.check_warnings(('', DeprecationWarning)): |
| 330 self.channel._SMTPChannel__conn = 'spam' | 450 self.channel._SMTPChannel__conn = 'spam' |
| 331 with support.check_warnings(('', PendingDeprecationWarning)): | 451 with support.check_warnings(('', DeprecationWarning)): |
| 332 spam = self.channel._SMTPChannel__addr | 452 spam = self.channel._SMTPChannel__addr |
| 333 with support.check_warnings(('', PendingDeprecationWarning)): | 453 with support.check_warnings(('', DeprecationWarning)): |
| 334 self.channel._SMTPChannel__addr = 'spam' | 454 self.channel._SMTPChannel__addr = 'spam' |
| 335 | 455 |
| 336 | 456 |
| 337 class SMTPDChannelWithDataSizeLimitTest(TestCase): | 457 class SMTPDChannelWithDataSizeLimitTest(unittest.TestCase): |
| 338 | 458 |
| 339 def setUp(self): | 459 def setUp(self): |
| 340 smtpd.socket = asyncore.socket = mock_socket | 460 smtpd.socket = asyncore.socket = mock_socket |
| 341 self.debug = smtpd.DEBUGSTREAM = io.StringIO() | 461 self.debug = smtpd.DEBUGSTREAM = io.StringIO() |
| 342 self.server = DummyServer('a', 'b') | 462 self.server = DummyServer('a', 'b') |
| 343 conn, addr = self.server.accept() | 463 conn, addr = self.server.accept() |
| 344 # Set DATA size limit to 32 bytes for easy testing | 464 # Set DATA size limit to 32 bytes for easy testing |
| 345 self.channel = smtpd.SMTPChannel(self.server, conn, addr) | 465 self.channel = smtpd.SMTPChannel(self.server, conn, addr, 32) |
| 346 self.channel.size_limit = 32 | |
| 347 | 466 |
| 348 def tearDown(self): | 467 def tearDown(self): |
| 349 asyncore.socket = smtpd.socket = socket | 468 asyncore.socket = smtpd.socket = socket |
| 350 | 469 |
| 351 def write_line(self, line): | 470 def write_line(self, line): |
| 352 self.channel.socket.queue_recv(line) | 471 self.channel.socket.queue_recv(line) |
| 353 self.channel.handle_read() | 472 self.channel.handle_read() |
| 354 | 473 |
| 355 def test_data_limit_dialog(self): | 474 def test_data_limit_dialog(self): |
| 356 self.write_line(b'MAIL From:eggs@example') | 475 self.write_line(b'MAIL From:eggs@example') |
| (...skipping 13 matching lines...) Expand all Loading... |
| 370 self.write_line(b'MAIL From:eggs@example') | 489 self.write_line(b'MAIL From:eggs@example') |
| 371 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') | 490 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') |
| 372 self.write_line(b'RCPT To:spam@example') | 491 self.write_line(b'RCPT To:spam@example') |
| 373 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') | 492 self.assertEqual(self.channel.socket.last, b'250 OK\r\n') |
| 374 | 493 |
| 375 self.write_line(b'DATA') | 494 self.write_line(b'DATA') |
| 376 self.assertEqual(self.channel.socket.last, | 495 self.assertEqual(self.channel.socket.last, |
| 377 b'354 End data with <CR><LF>.<CR><LF>\r\n') | 496 b'354 End data with <CR><LF>.<CR><LF>\r\n') |
| 378 self.write_line(b'This message is longer than 32 bytes\r\n.') | 497 self.write_line(b'This message is longer than 32 bytes\r\n.') |
| 379 self.assertEqual(self.channel.socket.last, | 498 self.assertEqual(self.channel.socket.last, |
| 380 b'552 Too much mail data\r\n') | 499 b'552 Error: Too much mail data\r\n') |
| 381 | 500 |
| 382 | 501 |
| 383 def test_main(): | 502 def test_main(): |
| 384 support.run_unittest(SMTPDServerTest, SMTPDChannelTest, | 503 support.run_unittest(SMTPDServerTest, SMTPDChannelTest, |
| 385 SMTPDChannelWithDataSizeLimitTest) | 504 SMTPDChannelWithDataSizeLimitTest) |
| 386 | 505 |
| 387 if __name__ == "__main__": | 506 if __name__ == "__main__": |
| 388 test_main() | 507 test_main() |
| 389 | |
| LEFT | RIGHT |