140140_quoted = re .compile (br'"(?:[^"\\]|\\.)*+"' )
141141
142142
143+ def _paren_depth (data , depth = 0 ):
144+ # Net parenthesis nesting of data, ignoring parentheses in quoted strings.
145+ data = _quoted .sub (b'' , data )
146+ return depth + data .count (b'(' ) - data .count (b')' )
147+
148+
143149class IMAP4 :
144150
145151 r"""IMAP4 client class.
@@ -797,7 +803,7 @@ def select(self, mailbox='INBOX', readonly=False):
797803 if __debug__ :
798804 if self .debug >= 1 :
799805 self ._dump_ur (self .untagged_responses )
800- raise self .readonly ('%s is not writable' % mailbox )
806+ raise self .readonly ('%r is not writable' % ( mailbox ,) )
801807 return typ , self .untagged_responses .get ('EXISTS' , [None ])
802808
803809
@@ -921,7 +927,7 @@ def uid(self, command, *args):
921927 """
922928 command = command .upper ()
923929 if not command in Commands :
924- raise self .error ("Unknown IMAP4 UID command: %s " % command )
930+ raise self .error ("Unknown IMAP4 UID command: %r " % ( command ,) )
925931 if self .state not in Commands [command ]:
926932 raise self .error ("command %s illegal in state %s, "
927933 "only allowed in states %s" %
@@ -1154,6 +1160,11 @@ def _get_response(self):
11541160
11551161 resp = self ._get_line ()
11561162
1163+ # Skip spurious blank lines between responses (some servers send one
1164+ # after a literal that ends a response).
1165+ while resp == b'' :
1166+ resp = self ._get_line ()
1167+
11571168 # Command completion response?
11581169
11591170 if self ._match (self .tagre , resp ):
@@ -1191,6 +1202,7 @@ def _get_response(self):
11911202
11921203 # Is there a literal to come?
11931204
1205+ depth = 0 # open parenthesis nesting so far
11941206 while self ._match (self .Literal , dat ):
11951207
11961208 # Read literal direct from connection.
@@ -1204,13 +1216,15 @@ def _get_response(self):
12041216 # Store response with literal as tuple
12051217
12061218 self ._append_untagged (typ , (dat , data ))
1219+ depth = _paren_depth (dat , depth )
12071220
12081221 # Read trailer - possibly containing another literal
12091222
12101223 dat = self ._get_line ()
12111224
1212- # Skip a blank line that some servers send after a literal.
1213- if dat == b'' :
1225+ # Skip spurious blank lines after a literal, but only inside an
1226+ # unclosed parenthesis (at top level they end the response).
1227+ while dat == b'' and depth > 0 :
12141228 dat = self ._get_line ()
12151229
12161230 self ._append_untagged (typ , dat )
0 commit comments