Throw OfflineImapError when we try to request an inexistant message

During a sync run, someone might remove or move IMAP messages. As we
only cache the list of UIDs in the beginning, we might be requesting
UIDs that don't exist anymore. Protect folder.IMAP.getmessage() against
the response that we get when we ask for unknown UIDs.

Also, if the server responds with anything else than "OK", (eg. Gmail
seems to be saying frequently ['NO', 'Dave I can't let you do that now']
:-) so we should also be throwing OfflineImapErrors here rather than
AssertionErrors.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
Sebastian Spaeth 2011-06-17 08:56:30 +02:00 committed by Nicolas Sebrecht
parent 41fad17125
commit 7570f71880
3 changed files with 19 additions and 5 deletions

View File

@ -20,6 +20,9 @@ Bug Fixes
--------- ---------
* We protect more robustly against asking for inexistent messages from the
IMAP server, when someone else deletes or moves messages while we sync.
Pending for the next major release Pending for the next major release
================================== ==================================

View File

@ -23,7 +23,7 @@ import re
import time import time
from copy import copy from copy import copy
from Base import BaseFolder from Base import BaseFolder
from offlineimap import imaputil, imaplibutil from offlineimap import imaputil, imaplibutil, OfflineImapError
class IMAPFolder(BaseFolder): class IMAPFolder(BaseFolder):
def __init__(self, imapserver, name, visiblename, accountname, repository): def __init__(self, imapserver, name, visiblename, accountname, repository):
@ -195,13 +195,24 @@ class IMAPFolder(BaseFolder):
def getmessage(self, uid): def getmessage(self, uid):
"""Retrieve message with UID from the IMAP server (incl body) """Retrieve message with UID from the IMAP server (incl body)
:returns: the message body :returns: the message body or throws and OfflineImapError
(probably severity MESSAGE) if e.g. no message with
this UID could be found.
""" """
imapobj = self.imapserver.acquireconnection() imapobj = self.imapserver.acquireconnection()
try: try:
imapobj.select(self.getfullname(), readonly = 1) imapobj.select(self.getfullname(), readonly = 1)
res_type, data = imapobj.uid('fetch', '%d' % uid, '(BODY.PEEK[])') res_type, data = imapobj.uid('fetch', str(uid), '(BODY.PEEK[])')
assert res_type == 'OK', "Fetching message with UID '%d' failed" % uid if data == [None] or res_type != 'OK':
#IMAP server says bad request or UID does not exist
severity = OfflineImapError.ERROR.MESSAGE
reason = "IMAP server '%s' responded with '%s' to fetching "\
"message UID '%d'" % (self.getrepository(), res_type, uid)
if data == [None]:
#IMAP server did not find a message with this UID
reason = "IMAP server '%s' does not have a message "\
"with UID '%s'" % (self.getrepository(), uid)
raise OfflineImapError(reason, severity)
# data looks now e.g. [('320 (UID 17061 BODY[] # data looks now e.g. [('320 (UID 17061 BODY[]
# {2565}','msgbody....')] we only asked for one message, # {2565}','msgbody....')] we only asked for one message,
# and that msg is in data[0]. msbody is in [0][1] # and that msg is in data[0]. msbody is in [0][1]

View File

@ -301,7 +301,7 @@ class IMAPServer:
(self.hostname, self.reposname) (self.hostname, self.reposname)
raise OfflineImapError(reason, severity) raise OfflineImapError(reason, severity)
elif isinstance(e, SSLError) and e.errno == 1: elif SSLError and isinstance(e, SSLError) and e.errno == 1:
# SSL unknown protocol error # SSL unknown protocol error
# happens e.g. when connecting via SSL to a non-SSL service # happens e.g. when connecting via SSL to a non-SSL service
if self.port != 443: if self.port != 443: