»Core Development>Code coverage>Lib/test/test_smtpd.py

Python code coverage for Lib/test/test_smtpd.py

#countcontent
1n/aimport unittest
2n/aimport textwrap
3n/afrom test import support, mock_socket
4n/aimport socket
5n/aimport io
6n/aimport smtpd
7n/aimport asyncore
8n/a
9n/a
10n/aclass DummyServer(smtpd.SMTPServer):
11n/a def __init__(self, *args, **kwargs):
12n/a smtpd.SMTPServer.__init__(self, *args, **kwargs)
13n/a self.messages = []
14n/a if self._decode_data:
15n/a self.return_status = 'return status'
16n/a else:
17n/a self.return_status = b'return status'
18n/a
19n/a def process_message(self, peer, mailfrom, rcpttos, data, **kw):
20n/a self.messages.append((peer, mailfrom, rcpttos, data))
21n/a if data == self.return_status:
22n/a return '250 Okish'
23n/a if 'mail_options' in kw and 'SMTPUTF8' in kw['mail_options']:
24n/a return '250 SMTPUTF8 message okish'
25n/a
26n/a
27n/aclass DummyDispatcherBroken(Exception):
28n/a pass
29n/a
30n/a
31n/aclass BrokenDummyServer(DummyServer):
32n/a def listen(self, num):
33n/a raise DummyDispatcherBroken()
34n/a
35n/a
36n/aclass SMTPDServerTest(unittest.TestCase):
37n/a def setUp(self):
38n/a smtpd.socket = asyncore.socket = mock_socket
39n/a
40n/a def test_process_message_unimplemented(self):
41n/a server = smtpd.SMTPServer((support.HOST, 0), ('b', 0),
42n/a decode_data=True)
43n/a conn, addr = server.accept()
44n/a channel = smtpd.SMTPChannel(server, conn, addr, decode_data=True)
45n/a
46n/a def write_line(line):
47n/a channel.socket.queue_recv(line)
48n/a channel.handle_read()
49n/a
50n/a write_line(b'HELO example')
51n/a write_line(b'MAIL From:eggs@example')
52n/a write_line(b'RCPT To:spam@example')
53n/a write_line(b'DATA')
54n/a self.assertRaises(NotImplementedError, write_line, b'spam\r\n.\r\n')
55n/a
56n/a def test_decode_data_and_enable_SMTPUTF8_raises(self):
57n/a self.assertRaises(
58n/a ValueError,
59n/a smtpd.SMTPServer,
60n/a (support.HOST, 0),
61n/a ('b', 0),
62n/a enable_SMTPUTF8=True,
63n/a decode_data=True)
64n/a
65n/a def tearDown(self):
66n/a asyncore.close_all()
67n/a asyncore.socket = smtpd.socket = socket
68n/a
69n/a
70n/aclass DebuggingServerTest(unittest.TestCase):
71n/a
72n/a def setUp(self):
73n/a smtpd.socket = asyncore.socket = mock_socket
74n/a
75n/a def send_data(self, channel, data, enable_SMTPUTF8=False):
76n/a def write_line(line):
77n/a channel.socket.queue_recv(line)
78n/a channel.handle_read()
79n/a write_line(b'EHLO example')
80n/a if enable_SMTPUTF8:
81n/a write_line(b'MAIL From:eggs@example BODY=8BITMIME SMTPUTF8')
82n/a else:
83n/a write_line(b'MAIL From:eggs@example')
84n/a write_line(b'RCPT To:spam@example')
85n/a write_line(b'DATA')
86n/a write_line(data)
87n/a write_line(b'.')
88n/a
89n/a def test_process_message_with_decode_data_true(self):
90n/a server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0),
91n/a decode_data=True)
92n/a conn, addr = server.accept()
93n/a channel = smtpd.SMTPChannel(server, conn, addr, decode_data=True)
94n/a with support.captured_stdout() as s:
95n/a self.send_data(channel, b'From: test\n\nhello\n')
96n/a stdout = s.getvalue()
97n/a self.assertEqual(stdout, textwrap.dedent("""\
98n/a ---------- MESSAGE FOLLOWS ----------
99n/a From: test
100n/a X-Peer: peer-address
101n/a
102n/a hello
103n/a ------------ END MESSAGE ------------
104n/a """))
105n/a
106n/a def test_process_message_with_decode_data_false(self):
107n/a server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0))
108n/a conn, addr = server.accept()
109n/a channel = smtpd.SMTPChannel(server, conn, addr)
110n/a with support.captured_stdout() as s:
111n/a self.send_data(channel, b'From: test\n\nh\xc3\xa9llo\xff\n')
112n/a stdout = s.getvalue()
113n/a self.assertEqual(stdout, textwrap.dedent("""\
114n/a ---------- MESSAGE FOLLOWS ----------
115n/a b'From: test'
116n/a b'X-Peer: peer-address'
117n/a b''
118n/a b'h\\xc3\\xa9llo\\xff'
119n/a ------------ END MESSAGE ------------
120n/a """))
121n/a
122n/a def test_process_message_with_enable_SMTPUTF8_true(self):
123n/a server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0),
124n/a enable_SMTPUTF8=True)
125n/a conn, addr = server.accept()
126n/a channel = smtpd.SMTPChannel(server, conn, addr, enable_SMTPUTF8=True)
127n/a with support.captured_stdout() as s:
128n/a self.send_data(channel, b'From: test\n\nh\xc3\xa9llo\xff\n')
129n/a stdout = s.getvalue()
130n/a self.assertEqual(stdout, textwrap.dedent("""\
131n/a ---------- MESSAGE FOLLOWS ----------
132n/a b'From: test'
133n/a b'X-Peer: peer-address'
134n/a b''
135n/a b'h\\xc3\\xa9llo\\xff'
136n/a ------------ END MESSAGE ------------
137n/a """))
138n/a
139n/a def test_process_SMTPUTF8_message_with_enable_SMTPUTF8_true(self):
140n/a server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0),
141n/a enable_SMTPUTF8=True)
142n/a conn, addr = server.accept()
143n/a channel = smtpd.SMTPChannel(server, conn, addr, enable_SMTPUTF8=True)
144n/a with support.captured_stdout() as s:
145n/a self.send_data(channel, b'From: test\n\nh\xc3\xa9llo\xff\n',
146n/a enable_SMTPUTF8=True)
147n/a stdout = s.getvalue()
148n/a self.assertEqual(stdout, textwrap.dedent("""\
149n/a ---------- MESSAGE FOLLOWS ----------
150n/a mail options: ['BODY=8BITMIME', 'SMTPUTF8']
151n/a b'From: test'
152n/a b'X-Peer: peer-address'
153n/a b''
154n/a b'h\\xc3\\xa9llo\\xff'
155n/a ------------ END MESSAGE ------------
156n/a """))
157n/a
158n/a def tearDown(self):
159n/a asyncore.close_all()
160n/a asyncore.socket = smtpd.socket = socket
161n/a
162n/a
163n/aclass TestFamilyDetection(unittest.TestCase):
164n/a def setUp(self):
165n/a smtpd.socket = asyncore.socket = mock_socket
166n/a
167n/a def tearDown(self):
168n/a asyncore.close_all()
169n/a asyncore.socket = smtpd.socket = socket
170n/a
171n/a @unittest.skipUnless(support.IPV6_ENABLED, "IPv6 not enabled")
172n/a def test_socket_uses_IPv6(self):
173n/a server = smtpd.SMTPServer((support.HOSTv6, 0), (support.HOST, 0))
174n/a self.assertEqual(server.socket.family, socket.AF_INET6)
175n/a
176n/a def test_socket_uses_IPv4(self):
177n/a server = smtpd.SMTPServer((support.HOST, 0), (support.HOSTv6, 0))
178n/a self.assertEqual(server.socket.family, socket.AF_INET)
179n/a
180n/a
181n/aclass TestRcptOptionParsing(unittest.TestCase):
182n/a error_response = (b'555 RCPT TO parameters not recognized or not '
183n/a b'implemented\r\n')
184n/a
185n/a def setUp(self):
186n/a smtpd.socket = asyncore.socket = mock_socket
187n/a self.old_debugstream = smtpd.DEBUGSTREAM
188n/a self.debug = smtpd.DEBUGSTREAM = io.StringIO()
189n/a
190n/a def tearDown(self):
191n/a asyncore.close_all()
192n/a asyncore.socket = smtpd.socket = socket
193n/a smtpd.DEBUGSTREAM = self.old_debugstream
194n/a
195n/a def write_line(self, channel, line):
196n/a channel.socket.queue_recv(line)
197n/a channel.handle_read()
198n/a
199n/a def test_params_rejected(self):
200n/a server = DummyServer((support.HOST, 0), ('b', 0))
201n/a conn, addr = server.accept()
202n/a channel = smtpd.SMTPChannel(server, conn, addr)
203n/a self.write_line(channel, b'EHLO example')
204n/a self.write_line(channel, b'MAIL from: <foo@example.com> size=20')
205n/a self.write_line(channel, b'RCPT to: <foo@example.com> foo=bar')
206n/a self.assertEqual(channel.socket.last, self.error_response)
207n/a
208n/a def test_nothing_accepted(self):
209n/a server = DummyServer((support.HOST, 0), ('b', 0))
210n/a conn, addr = server.accept()
211n/a channel = smtpd.SMTPChannel(server, conn, addr)
212n/a self.write_line(channel, b'EHLO example')
213n/a self.write_line(channel, b'MAIL from: <foo@example.com> size=20')
214n/a self.write_line(channel, b'RCPT to: <foo@example.com>')
215n/a self.assertEqual(channel.socket.last, b'250 OK\r\n')
216n/a
217n/a
218n/aclass TestMailOptionParsing(unittest.TestCase):
219n/a error_response = (b'555 MAIL FROM parameters not recognized or not '
220n/a b'implemented\r\n')
221n/a
222n/a def setUp(self):
223n/a smtpd.socket = asyncore.socket = mock_socket
224n/a self.old_debugstream = smtpd.DEBUGSTREAM
225n/a self.debug = smtpd.DEBUGSTREAM = io.StringIO()
226n/a
227n/a def tearDown(self):
228n/a asyncore.close_all()
229n/a asyncore.socket = smtpd.socket = socket
230n/a smtpd.DEBUGSTREAM = self.old_debugstream
231n/a
232n/a def write_line(self, channel, line):
233n/a channel.socket.queue_recv(line)
234n/a channel.handle_read()
235n/a
236n/a def test_with_decode_data_true(self):
237n/a server = DummyServer((support.HOST, 0), ('b', 0), decode_data=True)
238n/a conn, addr = server.accept()
239n/a channel = smtpd.SMTPChannel(server, conn, addr, decode_data=True)
240n/a self.write_line(channel, b'EHLO example')
241n/a for line in [
242n/a b'MAIL from: <foo@example.com> size=20 SMTPUTF8',
243n/a b'MAIL from: <foo@example.com> size=20 SMTPUTF8 BODY=8BITMIME',
244n/a b'MAIL from: <foo@example.com> size=20 BODY=UNKNOWN',
245n/a b'MAIL from: <foo@example.com> size=20 body=8bitmime',
246n/a ]:
247n/a self.write_line(channel, line)
248n/a self.assertEqual(channel.socket.last, self.error_response)
249n/a self.write_line(channel, b'MAIL from: <foo@example.com> size=20')
250n/a self.assertEqual(channel.socket.last, b'250 OK\r\n')
251n/a
252n/a def test_with_decode_data_false(self):
253n/a server = DummyServer((support.HOST, 0), ('b', 0))
254n/a conn, addr = server.accept()
255n/a channel = smtpd.SMTPChannel(server, conn, addr)
256n/a self.write_line(channel, b'EHLO example')
257n/a for line in [
258n/a b'MAIL from: <foo@example.com> size=20 SMTPUTF8',
259n/a b'MAIL from: <foo@example.com> size=20 SMTPUTF8 BODY=8BITMIME',
260n/a ]:
261n/a self.write_line(channel, line)
262n/a self.assertEqual(channel.socket.last, self.error_response)
263n/a self.write_line(
264n/a channel,
265n/a b'MAIL from: <foo@example.com> size=20 SMTPUTF8 BODY=UNKNOWN')
266n/a self.assertEqual(
267n/a channel.socket.last,
268n/a b'501 Error: BODY can only be one of 7BIT, 8BITMIME\r\n')
269n/a self.write_line(
270n/a channel, b'MAIL from: <foo@example.com> size=20 body=8bitmime')
271n/a self.assertEqual(channel.socket.last, b'250 OK\r\n')
272n/a
273n/a def test_with_enable_smtputf8_true(self):
274n/a server = DummyServer((support.HOST, 0), ('b', 0), enable_SMTPUTF8=True)
275n/a conn, addr = server.accept()
276n/a channel = smtpd.SMTPChannel(server, conn, addr, enable_SMTPUTF8=True)
277n/a self.write_line(channel, b'EHLO example')
278n/a self.write_line(
279n/a channel,
280n/a b'MAIL from: <foo@example.com> size=20 body=8bitmime smtputf8')
281n/a self.assertEqual(channel.socket.last, b'250 OK\r\n')
282n/a
283n/a
284n/aclass SMTPDChannelTest(unittest.TestCase):
285n/a def setUp(self):
286n/a smtpd.socket = asyncore.socket = mock_socket
287n/a self.old_debugstream = smtpd.DEBUGSTREAM
288n/a self.debug = smtpd.DEBUGSTREAM = io.StringIO()
289n/a self.server = DummyServer((support.HOST, 0), ('b', 0),
290n/a decode_data=True)
291n/a conn, addr = self.server.accept()
292n/a self.channel = smtpd.SMTPChannel(self.server, conn, addr,
293n/a decode_data=True)
294n/a
295n/a def tearDown(self):
296n/a asyncore.close_all()
297n/a asyncore.socket = smtpd.socket = socket
298n/a smtpd.DEBUGSTREAM = self.old_debugstream
299n/a
300n/a def write_line(self, line):
301n/a self.channel.socket.queue_recv(line)
302n/a self.channel.handle_read()
303n/a
304n/a def test_broken_connect(self):
305n/a self.assertRaises(
306n/a DummyDispatcherBroken, BrokenDummyServer,
307n/a (support.HOST, 0), ('b', 0), decode_data=True)
308n/a
309n/a def test_decode_data_and_enable_SMTPUTF8_raises(self):
310n/a self.assertRaises(
311n/a ValueError, smtpd.SMTPChannel,
312n/a self.server, self.channel.conn, self.channel.addr,
313n/a enable_SMTPUTF8=True, decode_data=True)
314n/a
315n/a def test_server_accept(self):
316n/a self.server.handle_accept()
317n/a
318n/a def test_missing_data(self):
319n/a self.write_line(b'')
320n/a self.assertEqual(self.channel.socket.last,
321n/a b'500 Error: bad syntax\r\n')
322n/a
323n/a def test_EHLO(self):
324n/a self.write_line(b'EHLO example')
325n/a self.assertEqual(self.channel.socket.last, b'250 HELP\r\n')
326n/a
327n/a def test_EHLO_bad_syntax(self):
328n/a self.write_line(b'EHLO')
329n/a self.assertEqual(self.channel.socket.last,
330n/a b'501 Syntax: EHLO hostname\r\n')
331n/a
332n/a def test_EHLO_duplicate(self):
333n/a self.write_line(b'EHLO example')
334n/a self.write_line(b'EHLO example')
335n/a self.assertEqual(self.channel.socket.last,
336n/a b'503 Duplicate HELO/EHLO\r\n')
337n/a
338n/a def test_EHLO_HELO_duplicate(self):
339n/a self.write_line(b'EHLO example')
340n/a self.write_line(b'HELO example')
341n/a self.assertEqual(self.channel.socket.last,
342n/a b'503 Duplicate HELO/EHLO\r\n')
343n/a
344n/a def test_HELO(self):
345n/a name = smtpd.socket.getfqdn()
346n/a self.write_line(b'HELO example')
347n/a self.assertEqual(self.channel.socket.last,
348n/a '250 {}\r\n'.format(name).encode('ascii'))
349n/a
350n/a def test_HELO_EHLO_duplicate(self):
351n/a self.write_line(b'HELO example')
352n/a self.write_line(b'EHLO example')
353n/a self.assertEqual(self.channel.socket.last,
354n/a b'503 Duplicate HELO/EHLO\r\n')
355n/a
356n/a def test_HELP(self):
357n/a self.write_line(b'HELP')
358n/a self.assertEqual(self.channel.socket.last,
359n/a b'250 Supported commands: EHLO HELO MAIL RCPT ' + \
360n/a b'DATA RSET NOOP QUIT VRFY\r\n')
361n/a
362n/a def test_HELP_command(self):
363n/a self.write_line(b'HELP MAIL')
364n/a self.assertEqual(self.channel.socket.last,
365n/a b'250 Syntax: MAIL FROM: <address>\r\n')
366n/a
367n/a def test_HELP_command_unknown(self):
368n/a self.write_line(b'HELP SPAM')
369n/a self.assertEqual(self.channel.socket.last,
370n/a b'501 Supported commands: EHLO HELO MAIL RCPT ' + \
371n/a b'DATA RSET NOOP QUIT VRFY\r\n')
372n/a
373n/a def test_HELO_bad_syntax(self):
374n/a self.write_line(b'HELO')
375n/a self.assertEqual(self.channel.socket.last,
376n/a b'501 Syntax: HELO hostname\r\n')
377n/a
378n/a def test_HELO_duplicate(self):
379n/a self.write_line(b'HELO example')
380n/a self.write_line(b'HELO example')
381n/a self.assertEqual(self.channel.socket.last,
382n/a b'503 Duplicate HELO/EHLO\r\n')
383n/a
384n/a def test_HELO_parameter_rejected_when_extensions_not_enabled(self):
385n/a self.extended_smtp = False
386n/a self.write_line(b'HELO example')
387n/a self.write_line(b'MAIL from:<foo@example.com> SIZE=1234')
388n/a self.assertEqual(self.channel.socket.last,
389n/a b'501 Syntax: MAIL FROM: <address>\r\n')
390n/a
391n/a def test_MAIL_allows_space_after_colon(self):
392n/a self.write_line(b'HELO example')
393n/a self.write_line(b'MAIL from: <foo@example.com>')
394n/a self.assertEqual(self.channel.socket.last,
395n/a b'250 OK\r\n')
396n/a
397n/a def test_extended_MAIL_allows_space_after_colon(self):
398n/a self.write_line(b'EHLO example')
399n/a self.write_line(b'MAIL from: <foo@example.com> size=20')
400n/a self.assertEqual(self.channel.socket.last,
401n/a b'250 OK\r\n')
402n/a
403n/a def test_NOOP(self):
404n/a self.write_line(b'NOOP')
405n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
406n/a
407n/a def test_HELO_NOOP(self):
408n/a self.write_line(b'HELO example')
409n/a self.write_line(b'NOOP')
410n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
411n/a
412n/a def test_NOOP_bad_syntax(self):
413n/a self.write_line(b'NOOP hi')
414n/a self.assertEqual(self.channel.socket.last,
415n/a b'501 Syntax: NOOP\r\n')
416n/a
417n/a def test_QUIT(self):
418n/a self.write_line(b'QUIT')
419n/a self.assertEqual(self.channel.socket.last, b'221 Bye\r\n')
420n/a
421n/a def test_HELO_QUIT(self):
422n/a self.write_line(b'HELO example')
423n/a self.write_line(b'QUIT')
424n/a self.assertEqual(self.channel.socket.last, b'221 Bye\r\n')
425n/a
426n/a def test_QUIT_arg_ignored(self):
427n/a self.write_line(b'QUIT bye bye')
428n/a self.assertEqual(self.channel.socket.last, b'221 Bye\r\n')
429n/a
430n/a def test_bad_state(self):
431n/a self.channel.smtp_state = 'BAD STATE'
432n/a self.write_line(b'HELO example')
433n/a self.assertEqual(self.channel.socket.last,
434n/a b'451 Internal confusion\r\n')
435n/a
436n/a def test_command_too_long(self):
437n/a self.write_line(b'HELO example')
438n/a self.write_line(b'MAIL from: ' +
439n/a b'a' * self.channel.command_size_limit +
440n/a b'@example')
441n/a self.assertEqual(self.channel.socket.last,
442n/a b'500 Error: line too long\r\n')
443n/a
444n/a def test_MAIL_command_limit_extended_with_SIZE(self):
445n/a self.write_line(b'EHLO example')
446n/a fill_len = self.channel.command_size_limit - len('MAIL from:<@example>')
447n/a self.write_line(b'MAIL from:<' +
448n/a b'a' * fill_len +
449n/a b'@example> SIZE=1234')
450n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
451n/a
452n/a self.write_line(b'MAIL from:<' +
453n/a b'a' * (fill_len + 26) +
454n/a b'@example> SIZE=1234')
455n/a self.assertEqual(self.channel.socket.last,
456n/a b'500 Error: line too long\r\n')
457n/a
458n/a def test_MAIL_command_rejects_SMTPUTF8_by_default(self):
459n/a self.write_line(b'EHLO example')
460n/a self.write_line(
461n/a b'MAIL from: <naive@example.com> BODY=8BITMIME SMTPUTF8')
462n/a self.assertEqual(self.channel.socket.last[0:1], b'5')
463n/a
464n/a def test_data_longer_than_default_data_size_limit(self):
465n/a # Hack the default so we don't have to generate so much data.
466n/a self.channel.data_size_limit = 1048
467n/a self.write_line(b'HELO example')
468n/a self.write_line(b'MAIL From:eggs@example')
469n/a self.write_line(b'RCPT To:spam@example')
470n/a self.write_line(b'DATA')
471n/a self.write_line(b'A' * self.channel.data_size_limit +
472n/a b'A\r\n.')
473n/a self.assertEqual(self.channel.socket.last,
474n/a b'552 Error: Too much mail data\r\n')
475n/a
476n/a def test_MAIL_size_parameter(self):
477n/a self.write_line(b'EHLO example')
478n/a self.write_line(b'MAIL FROM:<eggs@example> SIZE=512')
479n/a self.assertEqual(self.channel.socket.last,
480n/a b'250 OK\r\n')
481n/a
482n/a def test_MAIL_invalid_size_parameter(self):
483n/a self.write_line(b'EHLO example')
484n/a self.write_line(b'MAIL FROM:<eggs@example> SIZE=invalid')
485n/a self.assertEqual(self.channel.socket.last,
486n/a b'501 Syntax: MAIL FROM: <address> [SP <mail-parameters>]\r\n')
487n/a
488n/a def test_MAIL_RCPT_unknown_parameters(self):
489n/a self.write_line(b'EHLO example')
490n/a self.write_line(b'MAIL FROM:<eggs@example> ham=green')
491n/a self.assertEqual(self.channel.socket.last,
492n/a b'555 MAIL FROM parameters not recognized or not implemented\r\n')
493n/a
494n/a self.write_line(b'MAIL FROM:<eggs@example>')
495n/a self.write_line(b'RCPT TO:<eggs@example> ham=green')
496n/a self.assertEqual(self.channel.socket.last,
497n/a b'555 RCPT TO parameters not recognized or not implemented\r\n')
498n/a
499n/a def test_MAIL_size_parameter_larger_than_default_data_size_limit(self):
500n/a self.channel.data_size_limit = 1048
501n/a self.write_line(b'EHLO example')
502n/a self.write_line(b'MAIL FROM:<eggs@example> SIZE=2096')
503n/a self.assertEqual(self.channel.socket.last,
504n/a b'552 Error: message size exceeds fixed maximum message size\r\n')
505n/a
506n/a def test_need_MAIL(self):
507n/a self.write_line(b'HELO example')
508n/a self.write_line(b'RCPT to:spam@example')
509n/a self.assertEqual(self.channel.socket.last,
510n/a b'503 Error: need MAIL command\r\n')
511n/a
512n/a def test_MAIL_syntax_HELO(self):
513n/a self.write_line(b'HELO example')
514n/a self.write_line(b'MAIL from eggs@example')
515n/a self.assertEqual(self.channel.socket.last,
516n/a b'501 Syntax: MAIL FROM: <address>\r\n')
517n/a
518n/a def test_MAIL_syntax_EHLO(self):
519n/a self.write_line(b'EHLO example')
520n/a self.write_line(b'MAIL from eggs@example')
521n/a self.assertEqual(self.channel.socket.last,
522n/a b'501 Syntax: MAIL FROM: <address> [SP <mail-parameters>]\r\n')
523n/a
524n/a def test_MAIL_missing_address(self):
525n/a self.write_line(b'HELO example')
526n/a self.write_line(b'MAIL from:')
527n/a self.assertEqual(self.channel.socket.last,
528n/a b'501 Syntax: MAIL FROM: <address>\r\n')
529n/a
530n/a def test_MAIL_chevrons(self):
531n/a self.write_line(b'HELO example')
532n/a self.write_line(b'MAIL from:<eggs@example>')
533n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
534n/a
535n/a def test_MAIL_empty_chevrons(self):
536n/a self.write_line(b'EHLO example')
537n/a self.write_line(b'MAIL from:<>')
538n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
539n/a
540n/a def test_MAIL_quoted_localpart(self):
541n/a self.write_line(b'EHLO example')
542n/a self.write_line(b'MAIL from: <"Fred Blogs"@example.com>')
543n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
544n/a self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
545n/a
546n/a def test_MAIL_quoted_localpart_no_angles(self):
547n/a self.write_line(b'EHLO example')
548n/a self.write_line(b'MAIL from: "Fred Blogs"@example.com')
549n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
550n/a self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
551n/a
552n/a def test_MAIL_quoted_localpart_with_size(self):
553n/a self.write_line(b'EHLO example')
554n/a self.write_line(b'MAIL from: <"Fred Blogs"@example.com> SIZE=1000')
555n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
556n/a self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
557n/a
558n/a def test_MAIL_quoted_localpart_with_size_no_angles(self):
559n/a self.write_line(b'EHLO example')
560n/a self.write_line(b'MAIL from: "Fred Blogs"@example.com SIZE=1000')
561n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
562n/a self.assertEqual(self.channel.mailfrom, '"Fred Blogs"@example.com')
563n/a
564n/a def test_nested_MAIL(self):
565n/a self.write_line(b'HELO example')
566n/a self.write_line(b'MAIL from:eggs@example')
567n/a self.write_line(b'MAIL from:spam@example')
568n/a self.assertEqual(self.channel.socket.last,
569n/a b'503 Error: nested MAIL command\r\n')
570n/a
571n/a def test_VRFY(self):
572n/a self.write_line(b'VRFY eggs@example')
573n/a self.assertEqual(self.channel.socket.last,
574n/a b'252 Cannot VRFY user, but will accept message and attempt ' + \
575n/a b'delivery\r\n')
576n/a
577n/a def test_VRFY_syntax(self):
578n/a self.write_line(b'VRFY')
579n/a self.assertEqual(self.channel.socket.last,
580n/a b'501 Syntax: VRFY <address>\r\n')
581n/a
582n/a def test_EXPN_not_implemented(self):
583n/a self.write_line(b'EXPN')
584n/a self.assertEqual(self.channel.socket.last,
585n/a b'502 EXPN not implemented\r\n')
586n/a
587n/a def test_no_HELO_MAIL(self):
588n/a self.write_line(b'MAIL from:<foo@example.com>')
589n/a self.assertEqual(self.channel.socket.last,
590n/a b'503 Error: send HELO first\r\n')
591n/a
592n/a def test_need_RCPT(self):
593n/a self.write_line(b'HELO example')
594n/a self.write_line(b'MAIL From:eggs@example')
595n/a self.write_line(b'DATA')
596n/a self.assertEqual(self.channel.socket.last,
597n/a b'503 Error: need RCPT command\r\n')
598n/a
599n/a def test_RCPT_syntax_HELO(self):
600n/a self.write_line(b'HELO example')
601n/a self.write_line(b'MAIL From: eggs@example')
602n/a self.write_line(b'RCPT to eggs@example')
603n/a self.assertEqual(self.channel.socket.last,
604n/a b'501 Syntax: RCPT TO: <address>\r\n')
605n/a
606n/a def test_RCPT_syntax_EHLO(self):
607n/a self.write_line(b'EHLO example')
608n/a self.write_line(b'MAIL From: eggs@example')
609n/a self.write_line(b'RCPT to eggs@example')
610n/a self.assertEqual(self.channel.socket.last,
611n/a b'501 Syntax: RCPT TO: <address> [SP <mail-parameters>]\r\n')
612n/a
613n/a def test_RCPT_lowercase_to_OK(self):
614n/a self.write_line(b'HELO example')
615n/a self.write_line(b'MAIL From: eggs@example')
616n/a self.write_line(b'RCPT to: <eggs@example>')
617n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
618n/a
619n/a def test_no_HELO_RCPT(self):
620n/a self.write_line(b'RCPT to eggs@example')
621n/a self.assertEqual(self.channel.socket.last,
622n/a b'503 Error: send HELO first\r\n')
623n/a
624n/a def test_data_dialog(self):
625n/a self.write_line(b'HELO example')
626n/a self.write_line(b'MAIL From:eggs@example')
627n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
628n/a self.write_line(b'RCPT To:spam@example')
629n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
630n/a
631n/a self.write_line(b'DATA')
632n/a self.assertEqual(self.channel.socket.last,
633n/a b'354 End data with <CR><LF>.<CR><LF>\r\n')
634n/a self.write_line(b'data\r\nmore\r\n.')
635n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
636n/a self.assertEqual(self.server.messages,
637n/a [(('peer-address', 'peer-port'),
638n/a 'eggs@example',
639n/a ['spam@example'],
640n/a 'data\nmore')])
641n/a
642n/a def test_DATA_syntax(self):
643n/a self.write_line(b'HELO example')
644n/a self.write_line(b'MAIL From:eggs@example')
645n/a self.write_line(b'RCPT To:spam@example')
646n/a self.write_line(b'DATA spam')
647n/a self.assertEqual(self.channel.socket.last, b'501 Syntax: DATA\r\n')
648n/a
649n/a def test_no_HELO_DATA(self):
650n/a self.write_line(b'DATA spam')
651n/a self.assertEqual(self.channel.socket.last,
652n/a b'503 Error: send HELO first\r\n')
653n/a
654n/a def test_data_transparency_section_4_5_2(self):
655n/a self.write_line(b'HELO example')
656n/a self.write_line(b'MAIL From:eggs@example')
657n/a self.write_line(b'RCPT To:spam@example')
658n/a self.write_line(b'DATA')
659n/a self.write_line(b'..\r\n.\r\n')
660n/a self.assertEqual(self.channel.received_data, '.')
661n/a
662n/a def test_multiple_RCPT(self):
663n/a self.write_line(b'HELO example')
664n/a self.write_line(b'MAIL From:eggs@example')
665n/a self.write_line(b'RCPT To:spam@example')
666n/a self.write_line(b'RCPT To:ham@example')
667n/a self.write_line(b'DATA')
668n/a self.write_line(b'data\r\n.')
669n/a self.assertEqual(self.server.messages,
670n/a [(('peer-address', 'peer-port'),
671n/a 'eggs@example',
672n/a ['spam@example','ham@example'],
673n/a 'data')])
674n/a
675n/a def test_manual_status(self):
676n/a # checks that the Channel is able to return a custom status message
677n/a self.write_line(b'HELO example')
678n/a self.write_line(b'MAIL From:eggs@example')
679n/a self.write_line(b'RCPT To:spam@example')
680n/a self.write_line(b'DATA')
681n/a self.write_line(b'return status\r\n.')
682n/a self.assertEqual(self.channel.socket.last, b'250 Okish\r\n')
683n/a
684n/a def test_RSET(self):
685n/a self.write_line(b'HELO example')
686n/a self.write_line(b'MAIL From:eggs@example')
687n/a self.write_line(b'RCPT To:spam@example')
688n/a self.write_line(b'RSET')
689n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
690n/a self.write_line(b'MAIL From:foo@example')
691n/a self.write_line(b'RCPT To:eggs@example')
692n/a self.write_line(b'DATA')
693n/a self.write_line(b'data\r\n.')
694n/a self.assertEqual(self.server.messages,
695n/a [(('peer-address', 'peer-port'),
696n/a 'foo@example',
697n/a ['eggs@example'],
698n/a 'data')])
699n/a
700n/a def test_HELO_RSET(self):
701n/a self.write_line(b'HELO example')
702n/a self.write_line(b'RSET')
703n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
704n/a
705n/a def test_RSET_syntax(self):
706n/a self.write_line(b'RSET hi')
707n/a self.assertEqual(self.channel.socket.last, b'501 Syntax: RSET\r\n')
708n/a
709n/a def test_unknown_command(self):
710n/a self.write_line(b'UNKNOWN_CMD')
711n/a self.assertEqual(self.channel.socket.last,
712n/a b'500 Error: command "UNKNOWN_CMD" not ' + \
713n/a b'recognized\r\n')
714n/a
715n/a def test_attribute_deprecations(self):
716n/a with support.check_warnings(('', DeprecationWarning)):
717n/a spam = self.channel._SMTPChannel__server
718n/a with support.check_warnings(('', DeprecationWarning)):
719n/a self.channel._SMTPChannel__server = 'spam'
720n/a with support.check_warnings(('', DeprecationWarning)):
721n/a spam = self.channel._SMTPChannel__line
722n/a with support.check_warnings(('', DeprecationWarning)):
723n/a self.channel._SMTPChannel__line = 'spam'
724n/a with support.check_warnings(('', DeprecationWarning)):
725n/a spam = self.channel._SMTPChannel__state
726n/a with support.check_warnings(('', DeprecationWarning)):
727n/a self.channel._SMTPChannel__state = 'spam'
728n/a with support.check_warnings(('', DeprecationWarning)):
729n/a spam = self.channel._SMTPChannel__greeting
730n/a with support.check_warnings(('', DeprecationWarning)):
731n/a self.channel._SMTPChannel__greeting = 'spam'
732n/a with support.check_warnings(('', DeprecationWarning)):
733n/a spam = self.channel._SMTPChannel__mailfrom
734n/a with support.check_warnings(('', DeprecationWarning)):
735n/a self.channel._SMTPChannel__mailfrom = 'spam'
736n/a with support.check_warnings(('', DeprecationWarning)):
737n/a spam = self.channel._SMTPChannel__rcpttos
738n/a with support.check_warnings(('', DeprecationWarning)):
739n/a self.channel._SMTPChannel__rcpttos = 'spam'
740n/a with support.check_warnings(('', DeprecationWarning)):
741n/a spam = self.channel._SMTPChannel__data
742n/a with support.check_warnings(('', DeprecationWarning)):
743n/a self.channel._SMTPChannel__data = 'spam'
744n/a with support.check_warnings(('', DeprecationWarning)):
745n/a spam = self.channel._SMTPChannel__fqdn
746n/a with support.check_warnings(('', DeprecationWarning)):
747n/a self.channel._SMTPChannel__fqdn = 'spam'
748n/a with support.check_warnings(('', DeprecationWarning)):
749n/a spam = self.channel._SMTPChannel__peer
750n/a with support.check_warnings(('', DeprecationWarning)):
751n/a self.channel._SMTPChannel__peer = 'spam'
752n/a with support.check_warnings(('', DeprecationWarning)):
753n/a spam = self.channel._SMTPChannel__conn
754n/a with support.check_warnings(('', DeprecationWarning)):
755n/a self.channel._SMTPChannel__conn = 'spam'
756n/a with support.check_warnings(('', DeprecationWarning)):
757n/a spam = self.channel._SMTPChannel__addr
758n/a with support.check_warnings(('', DeprecationWarning)):
759n/a self.channel._SMTPChannel__addr = 'spam'
760n/a
761n/a@unittest.skipUnless(support.IPV6_ENABLED, "IPv6 not enabled")
762n/aclass SMTPDChannelIPv6Test(SMTPDChannelTest):
763n/a def setUp(self):
764n/a smtpd.socket = asyncore.socket = mock_socket
765n/a self.old_debugstream = smtpd.DEBUGSTREAM
766n/a self.debug = smtpd.DEBUGSTREAM = io.StringIO()
767n/a self.server = DummyServer((support.HOSTv6, 0), ('b', 0),
768n/a decode_data=True)
769n/a conn, addr = self.server.accept()
770n/a self.channel = smtpd.SMTPChannel(self.server, conn, addr,
771n/a decode_data=True)
772n/a
773n/aclass SMTPDChannelWithDataSizeLimitTest(unittest.TestCase):
774n/a
775n/a def setUp(self):
776n/a smtpd.socket = asyncore.socket = mock_socket
777n/a self.old_debugstream = smtpd.DEBUGSTREAM
778n/a self.debug = smtpd.DEBUGSTREAM = io.StringIO()
779n/a self.server = DummyServer((support.HOST, 0), ('b', 0),
780n/a decode_data=True)
781n/a conn, addr = self.server.accept()
782n/a # Set DATA size limit to 32 bytes for easy testing
783n/a self.channel = smtpd.SMTPChannel(self.server, conn, addr, 32,
784n/a decode_data=True)
785n/a
786n/a def tearDown(self):
787n/a asyncore.close_all()
788n/a asyncore.socket = smtpd.socket = socket
789n/a smtpd.DEBUGSTREAM = self.old_debugstream
790n/a
791n/a def write_line(self, line):
792n/a self.channel.socket.queue_recv(line)
793n/a self.channel.handle_read()
794n/a
795n/a def test_data_limit_dialog(self):
796n/a self.write_line(b'HELO example')
797n/a self.write_line(b'MAIL From:eggs@example')
798n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
799n/a self.write_line(b'RCPT To:spam@example')
800n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
801n/a
802n/a self.write_line(b'DATA')
803n/a self.assertEqual(self.channel.socket.last,
804n/a b'354 End data with <CR><LF>.<CR><LF>\r\n')
805n/a self.write_line(b'data\r\nmore\r\n.')
806n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
807n/a self.assertEqual(self.server.messages,
808n/a [(('peer-address', 'peer-port'),
809n/a 'eggs@example',
810n/a ['spam@example'],
811n/a 'data\nmore')])
812n/a
813n/a def test_data_limit_dialog_too_much_data(self):
814n/a self.write_line(b'HELO example')
815n/a self.write_line(b'MAIL From:eggs@example')
816n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
817n/a self.write_line(b'RCPT To:spam@example')
818n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
819n/a
820n/a self.write_line(b'DATA')
821n/a self.assertEqual(self.channel.socket.last,
822n/a b'354 End data with <CR><LF>.<CR><LF>\r\n')
823n/a self.write_line(b'This message is longer than 32 bytes\r\n.')
824n/a self.assertEqual(self.channel.socket.last,
825n/a b'552 Error: Too much mail data\r\n')
826n/a
827n/a
828n/aclass SMTPDChannelWithDecodeDataFalse(unittest.TestCase):
829n/a
830n/a def setUp(self):
831n/a smtpd.socket = asyncore.socket = mock_socket
832n/a self.old_debugstream = smtpd.DEBUGSTREAM
833n/a self.debug = smtpd.DEBUGSTREAM = io.StringIO()
834n/a self.server = DummyServer((support.HOST, 0), ('b', 0))
835n/a conn, addr = self.server.accept()
836n/a self.channel = smtpd.SMTPChannel(self.server, conn, addr)
837n/a
838n/a def tearDown(self):
839n/a asyncore.close_all()
840n/a asyncore.socket = smtpd.socket = socket
841n/a smtpd.DEBUGSTREAM = self.old_debugstream
842n/a
843n/a def write_line(self, line):
844n/a self.channel.socket.queue_recv(line)
845n/a self.channel.handle_read()
846n/a
847n/a def test_ascii_data(self):
848n/a self.write_line(b'HELO example')
849n/a self.write_line(b'MAIL From:eggs@example')
850n/a self.write_line(b'RCPT To:spam@example')
851n/a self.write_line(b'DATA')
852n/a self.write_line(b'plain ascii text')
853n/a self.write_line(b'.')
854n/a self.assertEqual(self.channel.received_data, b'plain ascii text')
855n/a
856n/a def test_utf8_data(self):
857n/a self.write_line(b'HELO example')
858n/a self.write_line(b'MAIL From:eggs@example')
859n/a self.write_line(b'RCPT To:spam@example')
860n/a self.write_line(b'DATA')
861n/a self.write_line(b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87')
862n/a self.write_line(b'and some plain ascii')
863n/a self.write_line(b'.')
864n/a self.assertEqual(
865n/a self.channel.received_data,
866n/a b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87\n'
867n/a b'and some plain ascii')
868n/a
869n/a
870n/aclass SMTPDChannelWithDecodeDataTrue(unittest.TestCase):
871n/a
872n/a def setUp(self):
873n/a smtpd.socket = asyncore.socket = mock_socket
874n/a self.old_debugstream = smtpd.DEBUGSTREAM
875n/a self.debug = smtpd.DEBUGSTREAM = io.StringIO()
876n/a self.server = DummyServer((support.HOST, 0), ('b', 0),
877n/a decode_data=True)
878n/a conn, addr = self.server.accept()
879n/a # Set decode_data to True
880n/a self.channel = smtpd.SMTPChannel(self.server, conn, addr,
881n/a decode_data=True)
882n/a
883n/a def tearDown(self):
884n/a asyncore.close_all()
885n/a asyncore.socket = smtpd.socket = socket
886n/a smtpd.DEBUGSTREAM = self.old_debugstream
887n/a
888n/a def write_line(self, line):
889n/a self.channel.socket.queue_recv(line)
890n/a self.channel.handle_read()
891n/a
892n/a def test_ascii_data(self):
893n/a self.write_line(b'HELO example')
894n/a self.write_line(b'MAIL From:eggs@example')
895n/a self.write_line(b'RCPT To:spam@example')
896n/a self.write_line(b'DATA')
897n/a self.write_line(b'plain ascii text')
898n/a self.write_line(b'.')
899n/a self.assertEqual(self.channel.received_data, 'plain ascii text')
900n/a
901n/a def test_utf8_data(self):
902n/a self.write_line(b'HELO example')
903n/a self.write_line(b'MAIL From:eggs@example')
904n/a self.write_line(b'RCPT To:spam@example')
905n/a self.write_line(b'DATA')
906n/a self.write_line(b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87')
907n/a self.write_line(b'and some plain ascii')
908n/a self.write_line(b'.')
909n/a self.assertEqual(
910n/a self.channel.received_data,
911n/a 'utf8 enriched text: żźć\nand some plain ascii')
912n/a
913n/a
914n/aclass SMTPDChannelTestWithEnableSMTPUTF8True(unittest.TestCase):
915n/a def setUp(self):
916n/a smtpd.socket = asyncore.socket = mock_socket
917n/a self.old_debugstream = smtpd.DEBUGSTREAM
918n/a self.debug = smtpd.DEBUGSTREAM = io.StringIO()
919n/a self.server = DummyServer((support.HOST, 0), ('b', 0),
920n/a enable_SMTPUTF8=True)
921n/a conn, addr = self.server.accept()
922n/a self.channel = smtpd.SMTPChannel(self.server, conn, addr,
923n/a enable_SMTPUTF8=True)
924n/a
925n/a def tearDown(self):
926n/a asyncore.close_all()
927n/a asyncore.socket = smtpd.socket = socket
928n/a smtpd.DEBUGSTREAM = self.old_debugstream
929n/a
930n/a def write_line(self, line):
931n/a self.channel.socket.queue_recv(line)
932n/a self.channel.handle_read()
933n/a
934n/a def test_MAIL_command_accepts_SMTPUTF8_when_announced(self):
935n/a self.write_line(b'EHLO example')
936n/a self.write_line(
937n/a 'MAIL from: <naïve@example.com> BODY=8BITMIME SMTPUTF8'.encode(
938n/a 'utf-8')
939n/a )
940n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
941n/a
942n/a def test_process_smtputf8_message(self):
943n/a self.write_line(b'EHLO example')
944n/a for mail_parameters in [b'', b'BODY=8BITMIME SMTPUTF8']:
945n/a self.write_line(b'MAIL from: <a@example> ' + mail_parameters)
946n/a self.assertEqual(self.channel.socket.last[0:3], b'250')
947n/a self.write_line(b'rcpt to:<b@example.com>')
948n/a self.assertEqual(self.channel.socket.last[0:3], b'250')
949n/a self.write_line(b'data')
950n/a self.assertEqual(self.channel.socket.last[0:3], b'354')
951n/a self.write_line(b'c\r\n.')
952n/a if mail_parameters == b'':
953n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
954n/a else:
955n/a self.assertEqual(self.channel.socket.last,
956n/a b'250 SMTPUTF8 message okish\r\n')
957n/a
958n/a def test_utf8_data(self):
959n/a self.write_line(b'EHLO example')
960n/a self.write_line(
961n/a 'MAIL From: naïve@examplé BODY=8BITMIME SMTPUTF8'.encode('utf-8'))
962n/a self.assertEqual(self.channel.socket.last[0:3], b'250')
963n/a self.write_line('RCPT To:späm@examplé'.encode('utf-8'))
964n/a self.assertEqual(self.channel.socket.last[0:3], b'250')
965n/a self.write_line(b'DATA')
966n/a self.assertEqual(self.channel.socket.last[0:3], b'354')
967n/a self.write_line(b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87')
968n/a self.write_line(b'.')
969n/a self.assertEqual(
970n/a self.channel.received_data,
971n/a b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87')
972n/a
973n/a def test_MAIL_command_limit_extended_with_SIZE_and_SMTPUTF8(self):
974n/a self.write_line(b'ehlo example')
975n/a fill_len = (512 + 26 + 10) - len('mail from:<@example>')
976n/a self.write_line(b'MAIL from:<' +
977n/a b'a' * (fill_len + 1) +
978n/a b'@example>')
979n/a self.assertEqual(self.channel.socket.last,
980n/a b'500 Error: line too long\r\n')
981n/a self.write_line(b'MAIL from:<' +
982n/a b'a' * fill_len +
983n/a b'@example>')
984n/a self.assertEqual(self.channel.socket.last, b'250 OK\r\n')
985n/a
986n/a def test_multiple_emails_with_extended_command_length(self):
987n/a self.write_line(b'ehlo example')
988n/a fill_len = (512 + 26 + 10) - len('mail from:<@example>')
989n/a for char in [b'a', b'b', b'c']:
990n/a self.write_line(b'MAIL from:<' + char * fill_len + b'a@example>')
991n/a self.assertEqual(self.channel.socket.last[0:3], b'500')
992n/a self.write_line(b'MAIL from:<' + char * fill_len + b'@example>')
993n/a self.assertEqual(self.channel.socket.last[0:3], b'250')
994n/a self.write_line(b'rcpt to:<hans@example.com>')
995n/a self.assertEqual(self.channel.socket.last[0:3], b'250')
996n/a self.write_line(b'data')
997n/a self.assertEqual(self.channel.socket.last[0:3], b'354')
998n/a self.write_line(b'test\r\n.')
999n/a self.assertEqual(self.channel.socket.last[0:3], b'250')
1000n/a
1001n/a
1002n/aclass MiscTestCase(unittest.TestCase):
1003n/a def test__all__(self):
1004n/a blacklist = {
1005n/a "program", "Devnull", "DEBUGSTREAM", "NEWLINE", "COMMASPACE",
1006n/a "DATA_SIZE_DEFAULT", "usage", "Options", "parseargs",
1007n/a
1008n/a }
1009n/a support.check__all__(self, smtpd, blacklist=blacklist)
1010n/a
1011n/a
1012n/aif __name__ == "__main__":
1013n/a unittest.main()