Literal strings sent by IMAP servers are not correctly handled by imaplib2: an empty string is added.
How to reproduce:
:::python import imaplib2 import getpass from pprint import pprint server = 'imap.xxx.yyy' user = 'zzz' imapobj = imaplib2.IMAP4_SSL(server, debug=10) imapobj.login(user, getpass.getpass()) res_t, data = imapobj.list("INBOX", "*OLI*") pprint(data)
the ouput is:
36:18.14 MainThread [async] LIST ('INBOX', '*OLI*') 36:18.14 MainThread state_change_pending.acquire 36:18.14 MainThread state_change_pending.release 36:18.14 MainThread _request_push(ENFI3, LIST, {}) = ENFI3 36:18.14 MainThread data=ENFI3 LIST INBOX "*OLI*" 36:18.14 MainThread LIST:ENFI3.ready.wait 36:18.14 imap.ir5.eu writer > ENFI3 LIST INBOX "*OLI*"\r\n 36:18.15 imap.ir5.eu reader poll => [(3, 1)] 36:18.15 imap.ir5.eu reader rcvd 79 36:18.15 imap.ir5.eu reader < * LIST (\HasNoChildren) "." {17}\r\n 36:18.15 imap.ir5.eu reader < INBOX.OLItest "1"\r\n 36:18.15 imap.ir5.eu handler _put_response(* LIST (\HasNoChildren) "." {17}) 36:18.15 imap.ir5.eu handler read literal size 17 36:18.15 imap.ir5.eu reader < ENFI3 OK List completed.\r\n 36:18.15 imap.ir5.eu handler untagged_responses[LIST] 0 += ["('(\\HasNoChildren) "." {17}', 'INBOX.OLItest "1"')"] 36:18.15 imap.ir5.eu handler _put_response() 36:18.15 imap.ir5.eu handler untagged_responses[LIST] 1 += [""] 36:18.15 imap.ir5.eu handler literal completed 36:18.15 imap.ir5.eu handler _put_response(ENFI3 OK List completed.) 36:18.15 imap.ir5.eu handler state_change_free.set 36:18.15 imap.ir5.eu handler _request_pop(ENFI3, ('OK', ['List completed.'])) = ENFI3 36:18.15 imap.ir5.eu handler LIST:ENFI3.ready.set 36:18.15 MainThread _get_untagged_response(LIST) => [('(\\HasNoChildren) "." {17}', 'INBOX.OLItest "1"'), ''] 36:18.15 MainThread _untagged_response(OK, ?, LIST) => [('(\\HasNoChildren) "." {17}', 'INBOX.OLItest "1"'), ''] [('(\\HasNoChildren) "." {17}', 'INBOX.OLItest "1"'), '']
With the attached patch, the empty string is removed:
33:49.21 MainThread state_change_pending.release 33:49.21 MainThread [async] LIST ('INBOX', '*OLI*') 33:49.21 MainThread state_change_pending.acquire 33:49.21 MainThread state_change_pending.release 33:49.21 MainThread _request_push(DAME3, LIST, {}) = DAME3 33:49.21 MainThread data=DAME3 LIST INBOX "*OLI*" 33:49.21 MainThread LIST:DAME3.ready.wait 33:49.21 imap.ir5.eu writer > DAME3 LIST INBOX "*OLI*"\r\n 33:49.21 imap.ir5.eu reader poll => [(3, 1)] 33:49.21 imap.ir5.eu reader rcvd 79 33:49.21 imap.ir5.eu reader < * LIST (\HasNoChildren) "." {17}\r\n 33:49.21 imap.ir5.eu reader < INBOX.OLItest "1"\r\n 33:49.21 imap.ir5.eu handler _put_response(* LIST (\HasNoChildren) "." {17}) 33:49.21 imap.ir5.eu reader < DAME3 OK List completed.\r\n 33:49.21 imap.ir5.eu handler read literal size 17 33:49.21 imap.ir5.eu handler untagged_responses[LIST] 0 += ["('(\\HasNoChildren) "." {17}', 'INBOX.OLItest "1"')"] 33:49.21 imap.ir5.eu handler _put_response() 33:49.21 imap.ir5.eu handler literal completed 33:49.21 imap.ir5.eu handler _put_response(DAME3 OK List completed.) 33:49.21 imap.ir5.eu handler state_change_free.set 33:49.21 imap.ir5.eu handler _request_pop(DAME3, ('OK', ['List completed.'])) = DAME3 33:49.21 imap.ir5.eu handler LIST:DAME3.ready.set 33:49.21 MainThread _get_untagged_response(LIST) => [('(\\HasNoChildren) "." {17}', 'INBOX.OLItest "1"')] 33:49.21 MainThread _untagged_response(OK, ?, LIST) => [('(\\HasNoChildren) "." {17}', 'INBOX.OLItest "1"')] [('(\\HasNoChildren) "." {17}', 'INBOX.OLItest "1"')]
Thanks for yet another bug detection! However, that change breaks other responses where all expected data has been received but there is still a tail to retrieve. Please try changing your code to be just:
if dat:
self._append_untagged(typ, dat) # Tail
and see if that also fixes your example.
The updated patch is attached, it fixes my test case.
Excellent! Glad that worked, and thanks for another bug fix!