|
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 |
@@ -89,6 +91,18 @@ class Handler(SimpleIMAPHandler): |
89 | 91 | return Handler |
90 | 92 |
|
91 | 93 |
|
| 94 | +def _read_literal(handler, marker): |
| 95 | + # Read one literal, a raw octet sequence, by its count from the marker |
| 96 | + # ('{N}', or '(~{N}' in UTF8 mode). |
| 97 | + size = int(re.search(r'\{(\d+)\}', marker).group(1)) |
| 98 | + # The client must wait for the continuation, so nothing should be readable. |
| 99 | + if select.select([handler.connection], [], [], 0)[0]: |
| 100 | + raise AssertionError('client sent the literal before the ' |
| 101 | + 'continuation request') |
| 102 | + handler._send_textline('+') |
| 103 | + return handler.rfile.read(size) |
| 104 | + |
| 105 | + |
92 | 106 | class TestImaplib(unittest.TestCase): |
93 | 107 |
|
94 | 108 | def test_Internaldate2tuple(self): |
@@ -474,10 +488,8 @@ def cmd_AUTHENTICATE(self, tag, args): |
474 | 488 | self.server.response = yield |
475 | 489 | self._send_tagged(tag, 'OK', 'FAKEAUTH successful') |
476 | 490 | def cmd_APPEND(self, tag, args): |
477 | | - self._send_textline('+') |
478 | 491 | self.server.response = args |
479 | | - literal = yield |
480 | | - self.server.response.append(literal) |
| 492 | + self.server.response.append(_read_literal(self, args[-1])) |
481 | 493 | literal = yield |
482 | 494 | self.server.response.append(literal) |
483 | 495 | self._send_tagged(tag, 'OK', 'okay') |
@@ -737,6 +749,33 @@ def test_login(self): |
737 | 749 | self.assertEqual(data[0], b'LOGIN completed') |
738 | 750 | self.assertEqual(client.state, 'AUTH') |
739 | 751 |
|
| 752 | + def test_append_translate_line_endings(self): |
| 753 | + # By default line endings are normalized to CRLF; False sends the |
| 754 | + # literal exactly (gh-49680). |
| 755 | + class AppendHandler(SimpleIMAPHandler): |
| 756 | + def cmd_APPEND(self, tag, args): |
| 757 | + self.server.response = _read_literal(self, args[-1]) |
| 758 | + yield # read the trailer line |
| 759 | + self._send_tagged(tag, 'OK', 'APPEND completed') |
| 760 | + client, server = self._setup(AppendHandler) |
| 761 | + client.login('user', 'pass') |
| 762 | + message = b'a\rb\nc\r\nd' |
| 763 | + client.append('INBOX', None, None, message) |
| 764 | + self.assertEqual(server.response, b'a\r\nb\r\nc\r\nd') |
| 765 | + client.append('INBOX', None, None, message, |
| 766 | + translate_line_endings=False) |
| 767 | + self.assertEqual(server.response, message) |
| 768 | + |
| 769 | + # An email message uses bare LF by default; False sends it verbatim. |
| 770 | + message = EmailMessage() |
| 771 | + message['Subject'] = 'line endings' |
| 772 | + message.set_content('body line\n') |
| 773 | + message = message.as_bytes() |
| 774 | + self.assertNotIn(b'\r\n', message) |
| 775 | + client.append('INBOX', None, None, message, |
| 776 | + translate_line_endings=False) |
| 777 | + self.assertEqual(server.response, message) |
| 778 | + |
740 | 779 | def test_login_capabilities(self): |
741 | 780 | # A server may advertise new capabilities after login (as an |
742 | 781 | # untagged CAPABILITY response); imaplib must refresh its cached |
@@ -1673,10 +1712,8 @@ def test_enable_UTF8_True_append(self): |
1673 | 1712 |
|
1674 | 1713 | class UTF8AppendServer(self.UTF8Server): |
1675 | 1714 | def cmd_APPEND(self, tag, args): |
1676 | | - self._send_textline('+') |
1677 | 1715 | self.server.response = args |
1678 | | - literal = yield |
1679 | | - self.server.response.append(literal) |
| 1716 | + self.server.response.append(_read_literal(self, args[-1])) |
1680 | 1717 | literal = yield |
1681 | 1718 | self.server.response.append(literal) |
1682 | 1719 | self._send_tagged(tag, 'OK', 'okay') |
|
0 commit comments