From 71dd03e88c3feed6d1ff685a9246cfe2750330f5 Mon Sep 17 00:00:00 2001 From: Nicolas Sebrecht Date: Mon, 14 Sep 2015 14:56:17 +0200 Subject: [PATCH] Bump imaplib2 from 2.43 to 2.48 Signed-off-by: Nicolas Sebrecht --- offlineimap/imaplib2.py | 136 +++++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 58 deletions(-) diff --git a/offlineimap/imaplib2.py b/offlineimap/imaplib2.py index eeb4792..aa08a13 100644 --- a/offlineimap/imaplib2.py +++ b/offlineimap/imaplib2.py @@ -17,9 +17,9 @@ Public functions: Internaldate2Time __all__ = ("IMAP4", "IMAP4_SSL", "IMAP4_stream", "Internaldate2Time", "ParseFlags", "Time2Internaldate") -__version__ = "2.43" +__version__ = "2.48" __release__ = "2" -__revision__ = "43" +__revision__ = "48" __credits__ = """ Authentication code contributed by Donn Cave June 1998. String method conversion by ESR, February 2001. @@ -46,18 +46,23 @@ Fix for offlineimap "indexerror: string index out of range" bug provided by Eyge Fix for missing idle_lock in _handler() provided by Franklin Brook August 2014. Conversion to Python3 provided by F. Malina February 2015. Fix for READ-ONLY error from multiple EXAMINE/SELECT calls by Pierre-Louis Bonicoli March 2015. -Fix for null strings appended to untagged responses by Pierre-Louis Bonicoli March 2015.""" +Fix for null strings appended to untagged responses by Pierre-Louis Bonicoli March 2015. +Fix for correct byte encoding for _CRAM_MD5_AUTH taken from python3.5 imaplib.py June 2015. +Fix for correct Python 3 exception handling by Tobias Brink August 2015. +Fix to allow interruptible IDLE command by Tim Peoples September 2015.""" __author__ = "Piers Lauder " __URL__ = "http://imaplib2.sourceforge.net" __license__ = "Python License" import binascii, errno, os, random, re, select, socket, sys, time, threading, zlib -try: - import queue # py3 +if bytes != str: + # Python 3, but NB assumes strings in all I/O + # for backwards compatibility with python 2 usage. + import queue string_types = str -except ImportError: - import Queue as queue # py2 +else: + import Queue as queue string_types = basestring @@ -179,7 +184,7 @@ class Request(object): def get_response(self, exc_fmt=None): self.callback = None if __debug__: self.parent._log(3, '%s:%s.ready.wait' % (self.name, self.tag)) - self.ready.wait() + self.ready.wait(sys.float_info.max) if self.aborted is not None: typ, val = self.aborted @@ -441,19 +446,22 @@ class IMAP4(object): af, socktype, proto, canonname, sa = res try: s = socket.socket(af, socktype, proto) - except socket.error as msg: + except socket.error as m: + msg = m continue try: for i in (0, 1): try: s.connect(sa) break - except socket.error as msg: + except socket.error as m: + msg = m if len(msg.args) < 2 or msg.args[0] != errno.EINTR: raise else: raise socket.error(msg) - except socket.error as msg: + except socket.error as m: + msg = m s.close() continue break @@ -534,9 +542,9 @@ class IMAP4(object): data += self.compressor.flush(zlib.Z_SYNC_FLUSH) if bytes != str: - self.sock.sendall(bytes(data, 'utf8')) - else: - self.sock.sendall(data) + data = bytes(data, 'utf8') + + self.sock.sendall(data) def shutdown(self): @@ -881,7 +889,9 @@ class IMAP4(object): def _CRAM_MD5_AUTH(self, challenge): """Authobject to use with CRAM-MD5 authentication.""" import hmac - return self.user + " " + hmac.HMAC(self.password, challenge).hexdigest() + pwd = (self.password.encode('utf-8') if isinstance(self.password, str) + else self.password) + return self.user + " " + hmac.HMAC(pwd, challenge, 'md5').hexdigest() def logout(self, **kw): @@ -1229,7 +1239,7 @@ class IMAP4(object): self.commands_lock.release() - if __debug__: self._log(5, 'untagged_responses[%s] %s += ["%s"]' % (typ, len(urd)-1, dat)) + if __debug__: self._log(5, 'untagged_responses[%s] %s += ["%.80s"]' % (typ, len(urd)-1, dat)) def _check_bye(self): @@ -1297,7 +1307,7 @@ class IMAP4(object): self.commands_lock.release() if need_event: if __debug__: self._log(3, 'sync command %s waiting for empty commands Q' % name) - self.state_change_free.wait() + self.state_change_free.wait(sys.float_info.max) if __debug__: self._log(3, 'sync command %s proceeding' % name) if self.state not in Commands[name][CMD_VAL_STATES]: @@ -1316,7 +1326,7 @@ class IMAP4(object): while self._get_untagged_response(typ): continue - if self._get_untagged_response('READ-ONLY', leave=True) and not self.is_readonly: + if not self.is_readonly and self._get_untagged_response('READ-ONLY', leave=True): self.literal = None raise self.readonly('mailbox status changed to READ-ONLY') @@ -1402,7 +1412,7 @@ class IMAP4(object): def _command_completer(self, cb_arg_list): # Called for callback commands - (response, cb_arg, error) = cb_arg_list + response, cb_arg, error = cb_arg_list rqb, kw = cb_arg rqb.callback = kw['callback'] rqb.callback_arg = kw.get('cb_arg') @@ -1420,6 +1430,7 @@ class IMAP4(object): if __debug__: self._print_log() rqb.abort(self.error, '%s command error: %s %s. Data: %.100s' % (rqb.name, typ, dat, rqb.data)) return + if __debug__: self._log(4, '_command_completer(%s, %s, None) = %s' % (response, cb_arg, rqb.tag)) if 'untagged_response' in kw: response = self._untagged_response(typ, dat, kw['untagged_response']) rqb.deliver(response) @@ -1463,7 +1474,7 @@ class IMAP4(object): if not leave: del self.untagged_responses[i] self.commands_lock.release() - if __debug__: self._log(5, '_get_untagged_response(%s) => %s' % (name, dat)) + if __debug__: self._log(5, '_get_untagged_response(%s) => %.80s' % (name, dat)) return dat self.commands_lock.release() @@ -1605,11 +1616,17 @@ class IMAP4(object): self.commands_lock.acquire() rqb = self.tagged_commands.pop(name) if not self.tagged_commands: + need_event = True + else: + need_event = False + self.commands_lock.release() + + if __debug__: self._log(4, '_request_pop(%s, %s) [%d] = %s' % (name, data, len(self.tagged_commands), rqb.tag)) + rqb.deliver(data) + + if need_event: if __debug__: self._log(3, 'state_change_free.set') self.state_change_free.set() - self.commands_lock.release() - if __debug__: self._log(4, '_request_pop(%s, %s) = %s' % (name, data, rqb.tag)) - rqb.deliver(data) def _request_push(self, tag=None, name=None, **kw): @@ -1645,7 +1662,7 @@ class IMAP4(object): if not dat: break data += dat - if __debug__: self._log(4, '_untagged_response(%s, ?, %s) => %s' % (typ, name, data)) + if __debug__: self._log(4, '_untagged_response(%s, ?, %s) => %.80s' % (typ, name, data)) return typ, data @@ -1762,7 +1779,10 @@ class IMAP4(object): } return ' '.join([PollErrors[s] for s in PollErrors.keys() if (s & state)]) - line_part = '' + if bytes != str: + line_part = b'' + else: + line_part = '' poll = select.poll() @@ -1774,7 +1794,7 @@ class IMAP4(object): while not (terminate or self.Terminate): if self.state == LOGOUT: - timeout = 1 + timeout = 10 else: timeout = read_poll_timeout try: @@ -1802,11 +1822,11 @@ class IMAP4(object): if bytes != str: stop = data.find(b'\n', start) if stop < 0: - line_part += data[start:].decode() + line_part += data[start:] break stop += 1 line_part, start, line = \ - '', stop, line_part + data[start:stop].decode() + b'', stop, (line_part + data[start:stop]).decode(errors='ignore') else: stop = data.find('\n', start) if stop < 0: @@ -1846,7 +1866,10 @@ class IMAP4(object): if __debug__: self._log(1, 'starting using select') - line_part = '' + if bytes != str: + line_part = b'' + else: + line_part = '' rxzero = 0 terminate = False @@ -1878,11 +1901,11 @@ class IMAP4(object): if bytes != str: stop = data.find(b'\n', start) if stop < 0: - line_part += data[start:].decode() + line_part += data[start:] break stop += 1 line_part, start, line = \ - '', stop, line_part + data[start:stop].decode() + b'', stop, (line_part + data[start:stop]).decode(errors='ignore') else: stop = data.find('\n', start) if stop < 0: @@ -2100,27 +2123,18 @@ class IMAP4_SSL(IMAP4): data += self.compressor.flush(zlib.Z_SYNC_FLUSH) if bytes != str: - if hasattr(self.sock, "sendall"): - self.sock.sendall(bytes(data, 'utf8')) - else: - dlen = len(data) - while dlen > 0: - sent = self.sock.write(bytes(data, 'utf8')) - if sent == dlen: - break # avoid copy - data = data[sent:] - dlen = dlen - sent + data = bytes(data, 'utf8') + + if hasattr(self.sock, "sendall"): + self.sock.sendall(data) else: - if hasattr(self.sock, "sendall"): - self.sock.sendall(data) - else: - dlen = len(data) - while dlen > 0: - sent = self.sock.write(data) - if sent == dlen: - break # avoid copy - data = data[sent:] - dlen = dlen - sent + dlen = len(data) + while dlen > 0: + sent = self.sock.write(data) + if sent == dlen: + break # avoid copy + data = data[sent:] + dlen = dlen - sent def ssl(self): @@ -2195,9 +2209,9 @@ class IMAP4_stream(IMAP4): data += self.compressor.flush(zlib.Z_SYNC_FLUSH) if bytes != str: - self.writefile.write(bytes(data, 'utf8')) - else: - self.writefile.write(data) + data = bytes(data, 'utf8') + + self.writefile.write(data) self.writefile.flush() @@ -2372,8 +2386,14 @@ if __name__ == '__main__': # To test: invoke either as 'python imaplib2.py [IMAP4_server_hostname]', # or as 'python imaplib2.py -s "rsh IMAP4_server_hostname exec /etc/rimapd"' - # or as 'python imaplib2.py -l "keyfile[:certfile]" [IMAP4_SSL_server_hostname]' + # or as 'python imaplib2.py -l keyfile[:certfile]|: [IMAP4_SSL_server_hostname]' + # + # Option "-d " turns on debugging (use "-d 5" for everything) # Option "-i" tests that IDLE is interruptible + # Option "-p " allows alternate ports + + if not __debug__: + raise ValueError('Please run without -O') import getopt, getpass @@ -2446,10 +2466,10 @@ if __name__ == '__main__': ) - AsyncError = None + AsyncError, M = None, None def responder(cb_arg_list): - (response, cb_arg, error) = cb_arg_list + response, cb_arg, error = cb_arg_list global AsyncError cmd, args = cb_arg if error is not None: @@ -2569,7 +2589,7 @@ if __name__ == '__main__': print('All tests OK.') except: - if not idle_intr or not 'IDLE' in M.capabilities: + if not idle_intr or M is None or not 'IDLE' in M.capabilities: print('Tests failed.') if not debug: