|
2 | 2 | from test.support import socket_helper |
3 | 3 |
|
4 | 4 | from contextlib import contextmanager |
| 5 | +from email.message import EmailMessage |
5 | 6 | import imaplib |
6 | 7 | import os.path |
7 | 8 | import socketserver |
8 | 9 | import time |
9 | 10 | import calendar |
10 | 11 | import threading |
11 | 12 | import re |
| 13 | +import select |
12 | 14 | import socket |
13 | 15 |
|
14 | 16 | from test.support import verbose, run_with_tz, run_with_locale, cpython_only |
|
27 | 29 | CAFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "certdata", "pycacert.pem") |
28 | 30 |
|
29 | 31 |
|
| 32 | +def _read_literal(handler, marker): |
| 33 | + # Read one literal, a raw octet sequence, by its count from the marker |
| 34 | + # ('{N}', or '(~{N}' in UTF8 mode). |
| 35 | + size = int(re.search(r'\{(\d+)\}', marker).group(1)) |
| 36 | + # The client must wait for the continuation, so nothing should be readable. |
| 37 | + if select.select([handler.connection], [], [], 0)[0]: |
| 38 | + raise AssertionError('client sent the literal before the ' |
| 39 | + 'continuation request') |
| 40 | + handler._send_textline('+') |
| 41 | + return handler.rfile.read(size) |
| 42 | + |
| 43 | + |
30 | 44 | class TestImaplib(unittest.TestCase): |
31 | 45 |
|
32 | 46 | def test_Internaldate2tuple(self): |
@@ -371,10 +385,8 @@ def cmd_AUTHENTICATE(self, tag, args): |
371 | 385 | self.server.response = yield |
372 | 386 | self._send_tagged(tag, 'OK', 'FAKEAUTH successful') |
373 | 387 | def cmd_APPEND(self, tag, args): |
374 | | - self._send_textline('+') |
375 | 388 | self.server.response = args |
376 | | - literal = yield |
377 | | - self.server.response.append(literal) |
| 389 | + self.server.response.append(_read_literal(self, args[-1])) |
378 | 390 | literal = yield |
379 | 391 | self.server.response.append(literal) |
380 | 392 | self._send_tagged(tag, 'OK', 'okay') |
@@ -635,24 +647,32 @@ def test_login(self): |
635 | 647 | self.assertEqual(client.state, 'AUTH') |
636 | 648 |
|
637 | 649 | def test_append_translate_line_endings(self): |
638 | | - # By default line endings in the message are normalized to CRLF; |
639 | | - # translate_line_endings=False sends the literal exactly (gh-49680). |
| 650 | + # By default line endings are normalized to CRLF; False sends the |
| 651 | + # literal exactly (gh-49680). |
640 | 652 | class AppendHandler(SimpleIMAPHandler): |
641 | 653 | def cmd_APPEND(self, tag, args): |
642 | | - size = int(args[-1].strip('{}')) |
643 | | - self._send_textline('+') |
644 | | - self.server.response = self.rfile.read(size) |
645 | | - self.rfile.readline() # trailing CRLF after the literal |
| 654 | + self.server.response = _read_literal(self, args[-1]) |
| 655 | + yield # read the trailer line |
646 | 656 | self._send_tagged(tag, 'OK', 'APPEND completed') |
647 | | - message = b'a\rb\nc\r\nd' |
648 | 657 | client, server = self._setup(AppendHandler) |
649 | 658 | client.login('user', 'pass') |
| 659 | + message = b'a\rb\nc\r\nd' |
650 | 660 | client.append('INBOX', None, None, message) |
651 | 661 | self.assertEqual(server.response, b'a\r\nb\r\nc\r\nd') |
652 | 662 | client.append('INBOX', None, None, message, |
653 | 663 | translate_line_endings=False) |
654 | 664 | self.assertEqual(server.response, message) |
655 | 665 |
|
| 666 | + # An email message uses bare LF by default; False sends it verbatim. |
| 667 | + message = EmailMessage() |
| 668 | + message['Subject'] = 'line endings' |
| 669 | + message.set_content('body line\n') |
| 670 | + message = message.as_bytes() |
| 671 | + self.assertNotIn(b'\r\n', message) |
| 672 | + client.append('INBOX', None, None, message, |
| 673 | + translate_line_endings=False) |
| 674 | + self.assertEqual(server.response, message) |
| 675 | + |
656 | 676 | def test_logout(self): |
657 | 677 | client, _ = self._setup(SimpleIMAPHandler) |
658 | 678 | typ, data = client.login('user', 'pass') |
@@ -944,10 +964,8 @@ def test_enable_UTF8_True_append(self): |
944 | 964 |
|
945 | 965 | class UTF8AppendServer(self.UTF8Server): |
946 | 966 | def cmd_APPEND(self, tag, args): |
947 | | - self._send_textline('+') |
948 | 967 | self.server.response = args |
949 | | - literal = yield |
950 | | - self.server.response.append(literal) |
| 968 | + self.server.response.append(_read_literal(self, args[-1])) |
951 | 969 | literal = yield |
952 | 970 | self.server.response.append(literal) |
953 | 971 | self._send_tagged(tag, 'OK', 'okay') |
|
0 commit comments