Skip to content

Commit 4b356f6

Browse files
gh-88574: Skip spurious blank lines between responses in imaplib
Some servers send the spurious blank line even after a literal that ends a response (such as a mailbox name returned by LIST as a literal). It falls between untagged responses and is now skipped, so a LIST or LSUB reply with a literal name in every response is read correctly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 97548d7 commit 4b356f6

3 files changed

Lines changed: 34 additions & 4 deletions

File tree

Lib/imaplib.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,6 +1351,13 @@ def _get_response(self, start_timeout=False):
13511351
else:
13521352
resp = self._get_line()
13531353

1354+
# Skip spurious blank lines between responses. Some servers send one
1355+
# after a literal that ends a response (e.g. a mailbox name returned by
1356+
# LIST as a literal); it arrives here as an empty line before the next
1357+
# response.
1358+
while resp == b'':
1359+
resp = self._get_line()
1360+
13541361
# Command completion response?
13551362

13561363
if self._match(self.tagre, resp):

Lib/test/test_imaplib.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,30 @@ def cmd_LIST(self, tag, args):
10591059
self.assertEqual(data, [(b'(\\HasNoChildren) "/" {9}', b'My (box)"'),
10601060
b''])
10611061

1062+
def test_spurious_blank_lines_between_responses(self):
1063+
# Some servers send a spurious blank line after every literal, including
1064+
# one that terminates a response. Such blanks fall between the untagged
1065+
# responses and must be skipped, even when several arrive in a row (a
1066+
# LIST reply with a literal mailbox name in each response).
1067+
names = [b'My (box)"', b'Another', b'Third']
1068+
class Handler(SimpleIMAPHandler):
1069+
def cmd_LIST(self, tag, args):
1070+
for name in names:
1071+
self._send(b'* LIST (\\HasNoChildren) "/" {%d}\r\n'
1072+
% len(name))
1073+
self._send(name)
1074+
self._send(b'\r\n\r\n') # ends the response, then a blank
1075+
self._send_tagged(tag, 'OK', 'LIST completed')
1076+
client, _ = self._setup(Handler)
1077+
client.login('user', 'pass')
1078+
typ, data = client.list()
1079+
self.assertEqual(typ, 'OK')
1080+
self.assertEqual(data, [
1081+
(b'(\\HasNoChildren) "/" {9}', b'My (box)"'), b'',
1082+
(b'(\\HasNoChildren) "/" {7}', b'Another'), b'',
1083+
(b'(\\HasNoChildren) "/" {5}', b'Third'), b'',
1084+
])
1085+
10621086
def test_unselect(self):
10631087
client, server = self._setup(SimpleIMAPHandler)
10641088
client.login('user', 'pass')
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
:mod:`imaplib` no longer fails when a server sends a spurious blank line
2-
after the counted data of a literal. Such a blank line is now skipped,
3-
but only inside an unclosed parenthesis, so that a literal terminating a
4-
response (such as a mailbox name returned by ``LIST``) is not mistaken
5-
for it.
2+
after the counted data of a literal, including after a literal that
3+
terminates a response (such as a mailbox name returned by ``LIST``).
4+
Such blank lines are now skipped without swallowing the following line.

0 commit comments

Comments
 (0)