diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py index 8fd1b88..89848b2 100644 --- a/offlineimap/folder/IMAP.py +++ b/offlineimap/folder/IMAP.py @@ -71,7 +71,7 @@ class IMAPFolder(BaseFolder): try: # Primes untagged_responses self.selectro(imapobj) - return long(imapobj.untagged_responses['UIDVALIDITY'][0]) + return long(imapobj._get_untagged_response('UIDVALIDITY', True)[0]) finally: self.imapserver.releaseconnection(imapobj) @@ -82,17 +82,16 @@ class IMAPFolder(BaseFolder): imapobj = self.imapserver.acquireconnection() try: # Primes untagged_responses - imapobj.select(self.getfullname(), readonly = 1, force = 1) - try: - # 1. Some mail servers do not return an EXISTS response - # if the folder is empty. 2. ZIMBRA servers can return - # multiple EXISTS replies in the form 500, 1000, 1500, - # 1623 so check for potentially multiple replies. - maxmsgid = 0 - for msgid in imapobj.untagged_responses['EXISTS']: - maxmsgid = max(long(msgid), maxmsgid) - except KeyError: + imaptype, imapdata = imapobj.select(self.getfullname(), readonly = 1, force = 1) + # 1. Some mail servers do not return an EXISTS response + # if the folder is empty. 2. ZIMBRA servers can return + # multiple EXISTS replies in the form 500, 1000, 1500, + # 1623 so check for potentially multiple replies. + if imapdata == [None]: return True + maxmsgid = 0 + for msgid in imapdata: + maxmsgid = max(long(msgid), maxmsgid) # Different number of messages than last time? if maxmsgid != len(statusfolder.getmessagelist()): @@ -127,7 +126,7 @@ class IMAPFolder(BaseFolder): try: # Primes untagged_responses - imapobj.select(self.getfullname(), readonly = 1, force = 1) + imaptype, imapdata = imapobj.select(self.getfullname(), readonly = 1, force = 1) maxage = self.config.getdefaultint("Account " + self.accountname, "maxage", -1) maxsize = self.config.getdefaultint("Account " + self.accountname, "maxsize", -1) @@ -169,17 +168,20 @@ class IMAPFolder(BaseFolder): # No messages; return return else: - try: - # 1. Some mail servers do not return an EXISTS response - # if the folder is empty. 2. ZIMBRA servers can return - # multiple EXISTS replies in the form 500, 1000, 1500, - # 1623 so check for potentially multiple replies. - maxmsgid = 0 - for msgid in imapobj.untagged_responses['EXISTS']: - maxmsgid = max(long(msgid), maxmsgid) - messagesToFetch = '1:%d' % maxmsgid; - except KeyError: + # 1. Some mail servers do not return an EXISTS response + # if the folder is empty. 2. ZIMBRA servers can return + # multiple EXISTS replies in the form 500, 1000, 1500, + # 1623 so check for potentially multiple replies. + if imapdata == [None]: return + + maxmsgid = 0 + for msgid in imapdata: + maxmsgid = max(long(msgid), maxmsgid) + + maxmsgid = long(imapdata[0]) + messagesToFetch = '1:%d' % maxmsgid; + if maxmsgid < 1: #no messages; return return @@ -437,10 +439,11 @@ class IMAPFolder(BaseFolder): # get the new UID from the APPENDUID response, it could look like # OK [APPENDUID 38505 3955] APPEND completed # with 38505 bein folder UIDvalidity and 3955 the new UID - if not imapobj.untagged_responses.has_key('APPENDUID'): - self.ui.warn("Server supports UIDPLUS but got no APPENDUID appending a message.") + if not imapobj._get_untagged_response('APPENDUID', True): + self.ui.warn("Server supports UIDPLUS but got no APPENDUID " + "appending a message.") return 0 - uid = long(imapobj.untagged_responses['APPENDUID'][-1].split(' ')[1]) + uid = long(imapobj._get_untagged_response('APPENDUID', True)[-1].split(' ')[1]) else: # we don't support UIDPLUS diff --git a/offlineimap/imaplibutil.py b/offlineimap/imaplibutil.py index 46c207d..f902fce 100644 --- a/offlineimap/imaplibutil.py +++ b/offlineimap/imaplibutil.py @@ -17,6 +17,7 @@ import re, socket, time, subprocess from offlineimap.ui import getglobalui +import threading from offlineimap.imaplib2 import * # Import the symbols we need that aren't exported by default @@ -44,6 +45,8 @@ class IMAP4_Tunnel(IMAP4): 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 = '' @@ -66,11 +69,13 @@ class IMAP4_Tunnel(IMAP4): self.process.wait() -def new_mesg(self, s, secs=None): +def new_mesg(self, s, tn=None, secs=None): if secs is None: secs = time.time() + if tn is None: + tn = threading.currentThread().getName() tm = time.strftime('%M:%S', time.localtime(secs)) - getglobalui().debug('imap', ' %s.%02d %s' % (tm, (secs*100)%100, s)) + getglobalui().debug('imap', ' %s.%02d %s %s' % (tm, (secs*100)%100, tn, s)) class WrappedIMAP4_SSL(IMAP4_SSL): """Provides an improved version of the standard IMAP4_SSL @@ -85,7 +90,7 @@ class WrappedIMAP4_SSL(IMAP4_SSL): del kwargs['cacertfile'] IMAP4_SSL.__init__(self, *args, **kwargs) - def open(self, host = '', port = IMAP4_SSL_PORT): + def open(self, host=None, port=None): """Do whatever IMAP4_SSL would do in open, but call sslwrap with cert verification""" #IMAP4_SSL.open(self, host, port) uses the below 2 lines: @@ -148,6 +153,9 @@ class WrappedIMAP4_SSL(IMAP4_SSL): if error: raise ssl.SSLError("SSL Certificate host name mismatch: %s" % error) + # imaplib2 uses this to poll() + self.read_fd = self.sock.fileno() + #TODO: Done for now. We should implement a mutt-like behavior #that offers the users to accept a certificate (presenting a #fingerprint of it) (get via self.sslobj.getpeercert()), and @@ -263,6 +271,9 @@ class WrappedIMAP4(IMAP4): raise socket.error(last_error) self.file = self.sock.makefile('rb') + # imaplib2 uses this to poll() + self.read_fd = self.sock.fileno() + mustquote = re.compile(r"[^\w!#$%&'+,.:;<=>?^`|~-]") def Internaldate2epoch(resp): diff --git a/offlineimap/imapserver.py b/offlineimap/imapserver.py index 4e01a79..93fc4db 100644 --- a/offlineimap/imapserver.py +++ b/offlineimap/imapserver.py @@ -48,6 +48,8 @@ class UsefulIMAPMixIn: and self.is_readonly == readonly: # No change; return. return + # Wipe out all old responses, to maintain semantics with old imaplib2 + del self.untagged_responses[:] result = self.__class__.__bases__[1].select(self, mailbox, readonly) if result[0] != 'OK': raise ValueError, "Error from select: %s" % str(result) @@ -55,9 +57,10 @@ class UsefulIMAPMixIn: self.selectedfolder = mailbox else: self.selectedfolder = None + return result - def _mesg(self, s, secs=None): - imaplibutil.new_mesg(self, s, secs) + def _mesg(self, s, tn=None, secs=None): + imaplibutil.new_mesg(self, s, tn, secs) class UsefulIMAP4(UsefulIMAPMixIn, imaplibutil.WrappedIMAP4): # This is a hack around Darwin's implementation of realloc() (which