From: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:25:08 +0000 (+0200) Subject: [3.14] gh-49680: Test imaplib.IMAP4.append line-ending normalization (GH-152877)... X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c98e2634c4386e6a3c7969f798a999538c13e32c;p=thirdparty%2FPython%2Fcpython.git [3.14] gh-49680: Test imaplib.IMAP4.append line-ending normalization (GH-152877) (GH-152878) (cherry picked from commit ddb6539c4976b18895bb5c5cd37d197c60fb459d) (cherry picked from commit 6759fd82117a50c40a0911189eda1c8dfa93ad03) Co-authored-by: Serhiy Storchaka Co-authored-by: harjoth Co-authored-by: Claude Opus 4.8 (1M context) --- diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index b9bdb0e64984..b5a7950fe4e0 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -9,6 +9,7 @@ import time import calendar import threading import re +import select import socket from test.support import verbose, run_with_tz, run_with_locale, cpython_only @@ -90,6 +91,18 @@ def make_simple_handler(command, untagged_response=(), return Handler +def _read_literal(handler, marker): + # Read one literal, a raw octet sequence, by its count from the marker + # ('{N}', or '(~{N}' in UTF8 mode). + size = int(re.search(r'\{(\d+)\}', marker).group(1)) + # The client must wait for the continuation, so nothing should be readable. + if select.select([handler.connection], [], [], 0)[0]: + raise AssertionError('client sent the literal before the ' + 'continuation request') + handler._send_textline('+') + return handler.rfile.read(size) + + class TestImaplib(unittest.TestCase): def test_Internaldate2tuple(self): @@ -475,10 +488,8 @@ class NewIMAPTestsMixin: self.server.response = yield self._send_tagged(tag, 'OK', 'FAKEAUTH successful') def cmd_APPEND(self, tag, args): - self._send_textline('+') self.server.response = args - literal = yield - self.server.response.append(literal) + self.server.response.append(_read_literal(self, args[-1])) literal = yield self.server.response.append(literal) self._send_tagged(tag, 'OK', 'okay') @@ -743,6 +754,19 @@ class NewIMAPTestsMixin: self.assertEqual(data[0], b'LOGIN completed') self.assertEqual(client.state, 'AUTH') + def test_append_line_endings(self): + # append() normalizes bare CR and LF in the message to CRLF. + class AppendHandler(SimpleIMAPHandler): + def cmd_APPEND(self, tag, args): + self.server.response = _read_literal(self, args[-1]) + yield # read the trailer line + self._send_tagged(tag, 'OK', 'APPEND completed') + client, server = self._setup(AppendHandler) + client.login('user', 'pass') + message = b'a\rb\nc\r\nd' + client.append('INBOX', None, None, message) + self.assertEqual(server.response, b'a\r\nb\r\nc\r\nd') + def test_login_capabilities(self): # A server may advertise new capabilities after login (as an # untagged CAPABILITY response); imaplib must refresh its cached @@ -1651,10 +1675,8 @@ class ThreadedNetworkedTests(unittest.TestCase): class UTF8AppendServer(self.UTF8Server): def cmd_APPEND(self, tag, args): - self._send_textline('+') self.server.response = args - literal = yield - self.server.response.append(literal) + self.server.response.append(_read_literal(self, args[-1])) literal = yield self.server.response.append(literal) self._send_tagged(tag, 'OK', 'okay')