[PATCH 2/7] [imaplib2 readd] Revert "[imaplib2 removal] Revert "use latest version of imaplib2""
Ethan Glasser-Camp
ethan at betacantrips.com
Mon Nov 29 13:54:47 GMT 2010
This reverts commit 36e0971adb872841d0d22b87a5453991e5e6d7a7.
Signed-off-by: Ethan Glasser-Camp <ethan at betacantrips.com>
---
offlineimap/imaplib2.py | 155 +++++++++++++++++++++++++++++++++++-----------
1 files changed, 118 insertions(+), 37 deletions(-)
diff --git a/offlineimap/imaplib2.py b/offlineimap/imaplib2.py
index 2426754..2f7919b 100644
--- a/offlineimap/imaplib2.py
+++ b/offlineimap/imaplib2.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2.5
+#!/usr/bin/env python
"""Threaded IMAP4 client.
@@ -17,9 +17,9 @@ Public functions: Internaldate2Time
__all__ = ("IMAP4", "IMAP4_SSL", "IMAP4_stream",
"Internaldate2Time", "ParseFlags", "Time2Internaldate")
-__version__ = "2.6"
+__version__ = "2.11"
__release__ = "2"
-__revision__ = "6"
+__revision__ = "11"
__credits__ = """
Authentication code contributed by Donn Cave <donn at u.washington.edu> June 1998.
String method conversion by ESR, February 2001.
@@ -29,11 +29,11 @@ GET/SETQUOTA contributed by Andreas Zeidler <az at kreativkombinat.de> June 2002.
PROXYAUTH contributed by Rick Holbert <holbert.13 at osu.edu> November 2002.
IDLE via threads suggested by Philippe Normand <phil at respyre.org> January 2005.
GET/SETANNOTATION contributed by Tomas Lindroos <skitta at abo.fi> June 2005.
-New socket open code from http://www.python.org/doc/lib/socket-example.html."""
+COMPRESS/DEFLATE contributed by Bron Gondwana <brong at brong.net> May 2009."""
__author__ = "Piers Lauder <piers at janeelix.com>"
-# Source URL: http://www.cs.usyd.edu.au/~piers/python/imaplib2
+__URL__ = "http://janeelix.com/piers/python/imaplib2"
-import binascii, os, Queue, random, re, select, socket, sys, time, threading
+import binascii, os, Queue, random, re, select, socket, sys, time, threading, zlib
select_module = select
@@ -62,6 +62,7 @@ Commands = {
'CAPABILITY': ((NONAUTH, AUTH, SELECTED), True),
'CHECK': ((SELECTED,), True),
'CLOSE': ((SELECTED,), False),
+ 'COMPRESS': ((AUTH,), False),
'COPY': ((SELECTED,), True),
'CREATE': ((AUTH, SELECTED), True),
'DELETE': ((AUTH, SELECTED), True),
@@ -264,6 +265,9 @@ class IMAP4(object):
self._accumulated_data = [] # Message data accumulated so far
self._literal_expected = None # Message data descriptor
+ self.compressor = None # COMPRESS/DEFLATE if not None
+ self.decompressor = None
+
# Create unique tag for this session,
# and compile tagged response matcher.
@@ -358,7 +362,8 @@ class IMAP4(object):
def open_socket(self):
- """Open socket choosing first address family available."""
+ """open_socket()
+ Open socket choosing first address family available."""
msg = (-1, 'could not open socket')
for res in socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM):
@@ -379,17 +384,38 @@ class IMAP4(object):
return s
+ def start_compressing(self):
+ """start_compressing()
+ Enable deflate compression on the socket (RFC 4978)."""
+
+ # rfc 1951 - pure DEFLATE, so use -15 for both windows
+ self.decompressor = zlib.decompressobj(-15)
+ self.compressor = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15)
+
+
def read(self, size):
"""data = read(size)
Read at most 'size' bytes from remote."""
- return self.sock.recv(size)
+ if self.decompressor is None:
+ return self.sock.recv(size)
+
+ if self.decompressor.unconsumed_tail:
+ data = self.decompressor.unconsumed_tail
+ else:
+ data = self.sock.recv(8192)
+
+ return self.decompressor.decompress(data, size)
def send(self, data):
"""send(data)
Send 'data' to remote."""
+ if self.compressor is not None:
+ data = self.compressor.compress(data)
+ data += self.compressor.flush(zlib.Z_SYNC_FLUSH)
+
self.sock.sendall(data)
@@ -411,6 +437,22 @@ class IMAP4(object):
# Utility methods
+ def enable_compression(self):
+ """enable_compression()
+ Ask the server to start compressing the connection.
+ Should be called from user of this class after instantiation, as in:
+ if 'COMPRESS=DEFLATE' in imapobj.capabilities:
+ imapobj.enable_compression()"""
+
+ try:
+ typ, dat = self._simple_command('COMPRESS', 'DEFLATE')
+ if typ == 'OK':
+ self.start_compressing()
+ if __debug__: self._log(1, 'Enabled COMPRESS=DEFLATE')
+ finally:
+ self.state_change_pending.release()
+
+
def recent(self, **kw):
"""(typ, [data]) = recent()
Return most recent 'RECENT' responses if any exist,
@@ -433,7 +475,7 @@ class IMAP4(object):
typ, dat = self._untagged_response(code, [None], code.upper())
return self._deliver_dat(typ, dat, kw)
-
+
@@ -647,9 +689,9 @@ class IMAP4(object):
List mailbox names in directory matching pattern.
'data' is list of LIST responses.
- NB: for 'pattern':
- % matches all except separator ( so LIST "" "%" returns names at root)
- * matches all (so LIST "" "*" returns whole directory tree from root)"""
+ NB: for 'pattern':
+ % matches all except separator ( so LIST "" "%" returns names at root)
+ * matches all (so LIST "" "*" returns whole directory tree from root)"""
name = 'LIST'
kw['untagged_response'] = name
@@ -813,7 +855,7 @@ class IMAP4(object):
self.state = AUTH
if __debug__: self._log(1, 'state => AUTH')
if typ == 'BAD':
- self._deliver_exc(self.error, '%s command error: %s %s' % (name, typ, dat), kw)
+ self._deliver_exc(self.error, '%s command error: %s %s. Data: %.100s' % (name, typ, dat, mailbox), kw)
return self._deliver_dat(typ, dat, kw)
self.state = SELECTED
if __debug__: self._log(1, 'state => SELECTED')
@@ -1043,20 +1085,25 @@ class IMAP4(object):
literal = self.literal
if literal is not None:
self.literal = None
- if isinstance(literal, str):
+ if isinstance(literal, basestring):
literator = None
data = '%s {%s}' % (data, len(literal))
else:
literator = literal
+ if __debug__: self._log(4, 'data=%s' % data)
+
rqb.data = '%s%s' % (data, CRLF)
- self.ouq.put(rqb)
if literal is None:
+ self.ouq.put(rqb)
return rqb
+ # Must setup continuation expectancy *before* ouq.put
crqb = self._request_push(tag='continuation')
+ self.ouq.put(rqb)
+
while True:
# Wait for continuation response
@@ -1076,6 +1123,10 @@ class IMAP4(object):
if literal is None:
break
+ if literator is not None:
+ # Need new request for next continuation response
+ crqb = self._request_push(tag='continuation')
+
if __debug__: self._log(4, 'write literal size %s' % len(literal))
crqb.data = '%s%s' % (literal, CRLF)
self.ouq.put(crqb)
@@ -1083,10 +1134,6 @@ class IMAP4(object):
if literator is None:
break
- self.commands_lock.acquire()
- self.tagged_commands['continuation'] = crqb
- self.commands_lock.release()
-
return rqb
@@ -1098,7 +1145,7 @@ class IMAP4(object):
self._check_bye()
if typ == 'BAD':
if __debug__: self._print_log()
- raise self.error('%s command error: %s %s' % (rqb.name, typ, dat))
+ raise self.error('%s command error: %s %s. Data: %.100s' % (rqb.name, typ, dat, rqb.data))
if 'untagged_response' in kw:
return self._untagged_response(typ, dat, kw['untagged_response'])
return typ, dat
@@ -1122,12 +1169,11 @@ class IMAP4(object):
typ, dat = response
if typ == 'BAD':
if __debug__: self._print_log()
- rqb.abort(self.error, '%s command error: %s %s' % (rqb.name, typ, dat))
+ rqb.abort(self.error, '%s command error: %s %s. Data: %.100s' % (rqb.name, typ, dat, rqb.data))
return
if 'untagged_response' in kw:
- rqb.deliver(self._untagged_response(typ, dat, kw['untagged_response']))
- else:
- rqb.deliver(response)
+ response = self._untagged_response(typ, dat, kw['untagged_response'])
+ rqb.deliver(response)
def _deliver_dat(self, typ, dat, kw):
@@ -1147,12 +1193,13 @@ class IMAP4(object):
def _end_idle(self):
irqb = self.idle_rqb
- if irqb is not None:
- self.idle_rqb = None
- self.idle_timeout = None
- irqb.data = 'DONE%s' % CRLF
- self.ouq.put(irqb)
- if __debug__: self._log(2, 'server IDLE finished')
+ if irqb is None:
+ return
+ self.idle_rqb = None
+ self.idle_timeout = None
+ irqb.data = 'DONE%s' % CRLF
+ self.ouq.put(irqb)
+ if __debug__: self._log(2, 'server IDLE finished')
def _match(self, cre, s):
@@ -1337,7 +1384,7 @@ class IMAP4(object):
threading.currentThread().setName('hdlr')
- time.sleep(0.1) # Don't start handling before main thread ready
+ time.sleep(0.1) # Don't start handling before main thread ready
if __debug__: self._log(1, 'starting')
@@ -1366,7 +1413,7 @@ class IMAP4(object):
if line is None:
break
- if not isinstance(line, str):
+ if not isinstance(line, basestring):
typ, val = line
break
@@ -1664,7 +1711,12 @@ class IMAP4_SSL(IMAP4):
self.host = host is not None and host or ''
self.port = port is not None and port or IMAP4_SSL_PORT
self.sock = self.open_socket()
- self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile)
+
+ try:
+ import ssl
+ self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
+ except ImportError:
+ self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile)
self.read_fd = self.sock.fileno()
@@ -1673,13 +1725,25 @@ class IMAP4_SSL(IMAP4):
"""data = read(size)
Read at most 'size' bytes from remote."""
- return self.sslobj.read(size)
+ if self.decompressor is None:
+ return self.sslobj.read(size)
+
+ if self.decompressor.unconsumed_tail:
+ data = self.decompressor.unconsumed_tail
+ else:
+ data = self.sslobj.read(8192)
+
+ return self.decompressor.decompress(data, size)
def send(self, data):
"""send(data)
Send 'data' to remote."""
+ if self.compressor is not None:
+ data = self.compressor.compress(data)
+ data += self.compressor.flush(zlib.Z_SYNC_FLUSH)
+
# NB: socket.ssl needs a "sendall" method to match socket objects.
bytes = len(data)
while bytes > 0:
@@ -1736,12 +1800,24 @@ class IMAP4_stream(IMAP4):
def read(self, size):
"""Read 'size' bytes from remote."""
- return os.read(self.read_fd, size)
+ if self.decompressor is None:
+ return os.read(self.read_fd, size)
+
+ if self.decompressor.unconsumed_tail:
+ data = self.decompressor.unconsumed_tail
+ else:
+ data = os.read(self.read_fd, 8192)
+
+ return self.decompressor.decompress(data, size)
def send(self, data):
"""Send data to remote."""
+ if self.compressor is not None:
+ data = self.compressor.compress(data)
+ data += self.compressor.flush(zlib.Z_SYNC_FLUSH)
+
self.writefile.write(data)
self.writefile.flush()
@@ -1920,7 +1996,7 @@ if __name__ == '__main__':
import getopt, getpass
try:
- optlist, args = getopt.getopt(sys.argv[1:], 'd:l:s:p:')
+ optlist, args = getopt.getopt(sys.argv[1:], 'd:l:s:p:v')
except getopt.error, val:
optlist, args = (), ()
@@ -1938,6 +2014,9 @@ if __name__ == '__main__':
elif opt == '-s':
stream_command = val
if not args: args = (stream_command,)
+ elif opt == '-v':
+ print __version__
+ sys.exit(0)
if not args: args = ('',)
if not port: port = (keyfile is not None) and IMAP4_SSL_PORT or IMAP4_PORT
@@ -1947,7 +2026,7 @@ if __name__ == '__main__':
USER = getpass.getuser()
test_mesg = 'From: %(user)s at localhost%(lf)sSubject: IMAP4 test%(lf)s%(lf)s%(data)s' \
- % {'user':USER, 'lf':'\n', 'data':open(__file__).read()}
+ % {'user':USER, 'lf':'\n', 'data':open(__file__).read()}
test_seq1 = [
('list', ('""', '%')),
('create', ('/tmp/imaplib2_test.0',)),
@@ -2028,6 +2107,8 @@ if __name__ == '__main__':
test_seq1.insert(0, ('login', (USER, PASSWD)))
M._mesg('PROTOCOL_VERSION = %s' % M.PROTOCOL_VERSION)
M._mesg('CAPABILITIES = %r' % (M.capabilities,))
+ if 'COMPRESS=DEFLATE' in M.capabilities:
+ M.enable_compression()
for cmd,args in test_seq1:
run(cmd, args, cb=1)
--
1.7.1
More information about the OfflineIMAP-project
mailing list