Fix IMAP4_Tunnel to work with imaplib2

* IMAP4_Tunnel constructor should support base-class arguments, in
  order to support the timeout argument.

* IMAP4_Tunnel needs to store the member IMAP4.host, which is normally
  done in IMAP4.open().

* Update IMAP4_Tunnel.read() and IMAP4_Tunnel.send().  We turn on
  nonblocking mode for these sockets, so we can return immediately
  with whatever data is available.

Signed-off-by: Ethan Glasser-Camp <ethan@betacantrips.com>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
Ethan Glasser-Camp 2011-04-13 04:52:18 -04:00 committed by Nicolas Sebrecht
parent fdf22400b1
commit 9e734006f6
2 changed files with 34 additions and 12 deletions

View File

@ -22,6 +22,8 @@ Changes
Bug Fixes
---------
* Fix IMAP4 tunnel with imaplib2.
Pending for the next major release
==================================

View File

@ -15,6 +15,8 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os
import fcntl
import re
import socket
import time
@ -67,32 +69,50 @@ class IMAP4_Tunnel(UsefulIMAPMixIn, IMAP4):
tunnelcmd -- shell command to generate the tunnel.
The result will be in PREAUTH stage."""
def __init__(self, tunnelcmd):
IMAP4.__init__(self, tunnelcmd)
def __init__(self, tunnelcmd, **kwargs):
IMAP4.__init__(self, tunnelcmd, **kwargs)
def open(self, host, port):
"""The tunnelcmd comes in on host!"""
self.host = host
self.process = subprocess.Popen(host, shell=True, close_fds=True,
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
(self.outfd, self.infd) = (self.process.stdin, self.process.stdout)
# imaplib2 polls on this fd
self.read_fd = self.infd.fileno()
def read(self, size):
retval = ''
while len(retval) < size:
buf = self.infd.read(size - len(retval))
if not buf:
break
retval += buf
return retval
self.set_nonblocking(self.read_fd)
def readline(self):
return self.infd.readline()
def set_nonblocking(self, fd):
"Mark fd as nonblocking"
# get the file's current flag settings
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
# clear non-blocking mode from flags
fl = fl & ~os.O_NONBLOCK
fcntl.fcntl(fd, fcntl.F_SETFL, fl)
def read(self, size):
"""data = read(size)
Read at most 'size' bytes from remote."""
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):
if self.compressor is not None:
data = self.compressor.compress(data)
data += self.compressor.flush(zlib.Z_SYNC_FLUSH)
self.outfd.write(data)
def shutdown(self):
self.infd.close()
self.outfd.close()