Update to match semantics of new imaplib2
The biggest change here is that imapobj.untagged_responses is no longer a dictionary, but a list. To access it, I use the semi-private _get_untagged_response method. * offlineimap/folder/IMAP.py (IMAPFolder.quickchanged, IMAPFolder.cachemessagelist): imaplib2 now explicitly removes its EXISTS response on select(), so instead we use the return values from select() to get the number of messages. * offlineimap/imapserver.py (UsefulIMAPMixIn.select): imaplib2 now stores untagged_responses for different mailboxes, which confuses us because it seems like our mailboxes are "still" in read-only mode when we just re-opened them. Additionally, we have to return the value from imaplib2's select() so that the above thing works. * offlineimap/imapserver.py (UsefulIMAPMixIn._mesg): imaplib2 now calls _mesg with the name of a thread, so we display this information in debug output. This requires a corresponding change to imaplibutil.new_mesg. * offlineimap/imaplibutil.py: We override IMAP4_SSL.open, whose default arguments have changed, so update the default arguments. We also subclass imaplib.IMAP4 in a few different places, which now relies on having a read_fd file descriptor to poll on. Signed-off-by: Ethan Glasser-Camp <ethan@betacantrips.com> Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
parent
f9413226b8
commit
1bf4bee5e6
@ -71,7 +71,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
try:
|
try:
|
||||||
# Primes untagged_responses
|
# Primes untagged_responses
|
||||||
self.selectro(imapobj)
|
self.selectro(imapobj)
|
||||||
return long(imapobj.untagged_responses['UIDVALIDITY'][0])
|
return long(imapobj._get_untagged_response('UIDVALIDITY', True)[0])
|
||||||
finally:
|
finally:
|
||||||
self.imapserver.releaseconnection(imapobj)
|
self.imapserver.releaseconnection(imapobj)
|
||||||
|
|
||||||
@ -82,17 +82,16 @@ class IMAPFolder(BaseFolder):
|
|||||||
imapobj = self.imapserver.acquireconnection()
|
imapobj = self.imapserver.acquireconnection()
|
||||||
try:
|
try:
|
||||||
# Primes untagged_responses
|
# Primes untagged_responses
|
||||||
imapobj.select(self.getfullname(), readonly = 1, force = 1)
|
imaptype, imapdata = imapobj.select(self.getfullname(), readonly = 1, force = 1)
|
||||||
try:
|
# 1. Some mail servers do not return an EXISTS response
|
||||||
# 1. Some mail servers do not return an EXISTS response
|
# if the folder is empty. 2. ZIMBRA servers can return
|
||||||
# if the folder is empty. 2. ZIMBRA servers can return
|
# multiple EXISTS replies in the form 500, 1000, 1500,
|
||||||
# multiple EXISTS replies in the form 500, 1000, 1500,
|
# 1623 so check for potentially multiple replies.
|
||||||
# 1623 so check for potentially multiple replies.
|
if imapdata == [None]:
|
||||||
maxmsgid = 0
|
|
||||||
for msgid in imapobj.untagged_responses['EXISTS']:
|
|
||||||
maxmsgid = max(long(msgid), maxmsgid)
|
|
||||||
except KeyError:
|
|
||||||
return True
|
return True
|
||||||
|
maxmsgid = 0
|
||||||
|
for msgid in imapdata:
|
||||||
|
maxmsgid = max(long(msgid), maxmsgid)
|
||||||
|
|
||||||
# Different number of messages than last time?
|
# Different number of messages than last time?
|
||||||
if maxmsgid != len(statusfolder.getmessagelist()):
|
if maxmsgid != len(statusfolder.getmessagelist()):
|
||||||
@ -127,7 +126,7 @@ class IMAPFolder(BaseFolder):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Primes untagged_responses
|
# 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)
|
maxage = self.config.getdefaultint("Account " + self.accountname, "maxage", -1)
|
||||||
maxsize = self.config.getdefaultint("Account " + self.accountname, "maxsize", -1)
|
maxsize = self.config.getdefaultint("Account " + self.accountname, "maxsize", -1)
|
||||||
@ -169,17 +168,20 @@ class IMAPFolder(BaseFolder):
|
|||||||
# No messages; return
|
# No messages; return
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
try:
|
# 1. Some mail servers do not return an EXISTS response
|
||||||
# 1. Some mail servers do not return an EXISTS response
|
# if the folder is empty. 2. ZIMBRA servers can return
|
||||||
# if the folder is empty. 2. ZIMBRA servers can return
|
# multiple EXISTS replies in the form 500, 1000, 1500,
|
||||||
# multiple EXISTS replies in the form 500, 1000, 1500,
|
# 1623 so check for potentially multiple replies.
|
||||||
# 1623 so check for potentially multiple replies.
|
if imapdata == [None]:
|
||||||
maxmsgid = 0
|
|
||||||
for msgid in imapobj.untagged_responses['EXISTS']:
|
|
||||||
maxmsgid = max(long(msgid), maxmsgid)
|
|
||||||
messagesToFetch = '1:%d' % maxmsgid;
|
|
||||||
except KeyError:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
maxmsgid = 0
|
||||||
|
for msgid in imapdata:
|
||||||
|
maxmsgid = max(long(msgid), maxmsgid)
|
||||||
|
|
||||||
|
maxmsgid = long(imapdata[0])
|
||||||
|
messagesToFetch = '1:%d' % maxmsgid;
|
||||||
|
|
||||||
if maxmsgid < 1:
|
if maxmsgid < 1:
|
||||||
#no messages; return
|
#no messages; return
|
||||||
return
|
return
|
||||||
@ -437,10 +439,11 @@ class IMAPFolder(BaseFolder):
|
|||||||
# get the new UID from the APPENDUID response, it could look like
|
# get the new UID from the APPENDUID response, it could look like
|
||||||
# OK [APPENDUID 38505 3955] APPEND completed
|
# OK [APPENDUID 38505 3955] APPEND completed
|
||||||
# with 38505 bein folder UIDvalidity and 3955 the new UID
|
# with 38505 bein folder UIDvalidity and 3955 the new UID
|
||||||
if not imapobj.untagged_responses.has_key('APPENDUID'):
|
if not imapobj._get_untagged_response('APPENDUID', True):
|
||||||
self.ui.warn("Server supports UIDPLUS but got no APPENDUID appending a message.")
|
self.ui.warn("Server supports UIDPLUS but got no APPENDUID "
|
||||||
|
"appending a message.")
|
||||||
return 0
|
return 0
|
||||||
uid = long(imapobj.untagged_responses['APPENDUID'][-1].split(' ')[1])
|
uid = long(imapobj._get_untagged_response('APPENDUID', True)[-1].split(' ')[1])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# we don't support UIDPLUS
|
# we don't support UIDPLUS
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
import re, socket, time, subprocess
|
import re, socket, time, subprocess
|
||||||
from offlineimap.ui import getglobalui
|
from offlineimap.ui import getglobalui
|
||||||
|
import threading
|
||||||
from offlineimap.imaplib2 import *
|
from offlineimap.imaplib2 import *
|
||||||
|
|
||||||
# Import the symbols we need that aren't exported by default
|
# 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,
|
self.process = subprocess.Popen(host, shell=True, close_fds=True,
|
||||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||||
(self.outfd, self.infd) = (self.process.stdin, self.process.stdout)
|
(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):
|
def read(self, size):
|
||||||
retval = ''
|
retval = ''
|
||||||
@ -66,11 +69,13 @@ class IMAP4_Tunnel(IMAP4):
|
|||||||
self.process.wait()
|
self.process.wait()
|
||||||
|
|
||||||
|
|
||||||
def new_mesg(self, s, secs=None):
|
def new_mesg(self, s, tn=None, secs=None):
|
||||||
if secs is None:
|
if secs is None:
|
||||||
secs = time.time()
|
secs = time.time()
|
||||||
|
if tn is None:
|
||||||
|
tn = threading.currentThread().getName()
|
||||||
tm = time.strftime('%M:%S', time.localtime(secs))
|
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):
|
class WrappedIMAP4_SSL(IMAP4_SSL):
|
||||||
"""Provides an improved version of the standard IMAP4_SSL
|
"""Provides an improved version of the standard IMAP4_SSL
|
||||||
@ -85,7 +90,7 @@ class WrappedIMAP4_SSL(IMAP4_SSL):
|
|||||||
del kwargs['cacertfile']
|
del kwargs['cacertfile']
|
||||||
IMAP4_SSL.__init__(self, *args, **kwargs)
|
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
|
"""Do whatever IMAP4_SSL would do in open, but call sslwrap
|
||||||
with cert verification"""
|
with cert verification"""
|
||||||
#IMAP4_SSL.open(self, host, port) uses the below 2 lines:
|
#IMAP4_SSL.open(self, host, port) uses the below 2 lines:
|
||||||
@ -148,6 +153,9 @@ class WrappedIMAP4_SSL(IMAP4_SSL):
|
|||||||
if error:
|
if error:
|
||||||
raise ssl.SSLError("SSL Certificate host name mismatch: %s" % 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
|
#TODO: Done for now. We should implement a mutt-like behavior
|
||||||
#that offers the users to accept a certificate (presenting a
|
#that offers the users to accept a certificate (presenting a
|
||||||
#fingerprint of it) (get via self.sslobj.getpeercert()), and
|
#fingerprint of it) (get via self.sslobj.getpeercert()), and
|
||||||
@ -263,6 +271,9 @@ class WrappedIMAP4(IMAP4):
|
|||||||
raise socket.error(last_error)
|
raise socket.error(last_error)
|
||||||
self.file = self.sock.makefile('rb')
|
self.file = self.sock.makefile('rb')
|
||||||
|
|
||||||
|
# imaplib2 uses this to poll()
|
||||||
|
self.read_fd = self.sock.fileno()
|
||||||
|
|
||||||
mustquote = re.compile(r"[^\w!#$%&'+,.:;<=>?^`|~-]")
|
mustquote = re.compile(r"[^\w!#$%&'+,.:;<=>?^`|~-]")
|
||||||
|
|
||||||
def Internaldate2epoch(resp):
|
def Internaldate2epoch(resp):
|
||||||
|
@ -48,6 +48,8 @@ class UsefulIMAPMixIn:
|
|||||||
and self.is_readonly == readonly:
|
and self.is_readonly == readonly:
|
||||||
# No change; return.
|
# No change; return.
|
||||||
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)
|
result = self.__class__.__bases__[1].select(self, mailbox, readonly)
|
||||||
if result[0] != 'OK':
|
if result[0] != 'OK':
|
||||||
raise ValueError, "Error from select: %s" % str(result)
|
raise ValueError, "Error from select: %s" % str(result)
|
||||||
@ -55,9 +57,10 @@ class UsefulIMAPMixIn:
|
|||||||
self.selectedfolder = mailbox
|
self.selectedfolder = mailbox
|
||||||
else:
|
else:
|
||||||
self.selectedfolder = None
|
self.selectedfolder = None
|
||||||
|
return result
|
||||||
|
|
||||||
def _mesg(self, s, secs=None):
|
def _mesg(self, s, tn=None, secs=None):
|
||||||
imaplibutil.new_mesg(self, s, secs)
|
imaplibutil.new_mesg(self, s, tn, secs)
|
||||||
|
|
||||||
class UsefulIMAP4(UsefulIMAPMixIn, imaplibutil.WrappedIMAP4):
|
class UsefulIMAP4(UsefulIMAPMixIn, imaplibutil.WrappedIMAP4):
|
||||||
# This is a hack around Darwin's implementation of realloc() (which
|
# This is a hack around Darwin's implementation of realloc() (which
|
||||||
|
Loading…
Reference in New Issue
Block a user