[PATCH] Throw OfflineImapError when we try to request an inexistant message

Sebastian Spaeth Sebastian at SSpaeth.de
Fri Jun 17 07:56:30 BST 2011


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 at SSpaeth.de>
---
Interdiff patch between v2 and v3 (we check for the existence of SSLError):
-   elif isinstance(e, SSLError) and e.errno == 1:
+   elif SSLError and isinstance(e, SSLError) and e.errno == 1:


 Changelog.draft.rst        |    3 +++
 offlineimap/folder/IMAP.py |   19 +++++++++++++++----
 offlineimap/imapserver.py  |    2 +-
 3 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/Changelog.draft.rst b/Changelog.draft.rst
index fd445ac..3d20bcf 100644
--- a/Changelog.draft.rst
+++ b/Changelog.draft.rst
@@ -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
 ==================================
 
diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py
index 8851b5b..62f220f 100644
--- a/offlineimap/folder/IMAP.py
+++ b/offlineimap/folder/IMAP.py
@@ -23,7 +23,7 @@ import re
 import time
 from copy import copy
 from Base import BaseFolder
-from offlineimap import imaputil, imaplibutil
+from offlineimap import imaputil, imaplibutil, OfflineImapError
 
 class IMAPFolder(BaseFolder):
     def __init__(self, imapserver, name, visiblename, accountname, repository):
@@ -195,13 +195,24 @@ class IMAPFolder(BaseFolder):
     def getmessage(self, uid):
         """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()
         try:
             imapobj.select(self.getfullname(), readonly = 1)
-            res_type, data = imapobj.uid('fetch', '%d' % uid, '(BODY.PEEK[])')
-            assert res_type == 'OK', "Fetching message with UID '%d' failed" % uid
+            res_type, data = imapobj.uid('fetch', str(uid), '(BODY.PEEK[])')
+            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[]
             # {2565}','msgbody....')]  we only asked for one message,
             # and that msg is in data[0]. msbody is in [0][1]
diff --git a/offlineimap/imapserver.py b/offlineimap/imapserver.py
index 0aaeca6..1eb8f6b 100644
--- a/offlineimap/imapserver.py
+++ b/offlineimap/imapserver.py
@@ -301,7 +301,7 @@ class IMAPServer:
                          (self.hostname, self.reposname)
                 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
                 # happens e.g. when connecting via SSL to a non-SSL service
                 if self.port != 443:
-- 
1.7.4.1





More information about the OfflineIMAP-project mailing list