[PATCH 2/2] Error proof IMAP.APPEND against dropped connections

Sebastian Spaeth Sebastian at SSpaeth.de
Tue Sep 6 12:19:26 BST 2011


Make sure that when a connection is dropped during append, we really
discard the broken connection and get a new one, retrying. We retry
indefinitely on the specific abort() Exception, as this is what imaplib2
suggests us to do.

Signed-off-by: Sebastian Spaeth <Sebastian at SSpaeth.de>
---
 offlineimap/folder/IMAP.py |   96 ++++++++++++++++++++++++--------------------
 1 files changed, 53 insertions(+), 43 deletions(-)

diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py
index dec14ae..82e472b 100644
--- a/offlineimap/folder/IMAP.py
+++ b/offlineimap/folder/IMAP.py
@@ -21,6 +21,7 @@ import random
 import binascii
 import re
 import time
+from sys import exc_info
 from copy import copy
 from Base import BaseFolder
 from offlineimap import imaputil, imaplibutil, OfflineImapError
@@ -495,53 +496,62 @@ class IMAPFolder(BaseFolder):
             self.savemessageflags(uid, flags)
             return uid
 
+        imapobj = self.imapserver.acquireconnection()
         try:
-            imapobj = self.imapserver.acquireconnection()
-
-            try:
-                imapobj.select(self.getfullname()) # Needed for search and making the box READ-WRITE
-            except imapobj.readonly:
-                # readonly exception. Return original uid to notify that
-                # we did not save the message. (see savemessage in Base.py)
-                self.ui.msgtoreadonly(self, uid, content, flags)
-                return uid
-
-            # UIDPLUS extension provides us with an APPENDUID response to our append()
-            use_uidplus = 'UIDPLUS' in imapobj.capabilities
-
-            # get the date of the message file, so we can pass it to the server.
-            date = self.getmessageinternaldate(content, rtime)
-            content = re.sub("(?<!\r)\n", "\r\n", content)
-
-            if not use_uidplus:
-                # insert a random unique header that we can fetch later
-                (headername, headervalue) = self.generate_randomheader(content)
-                self.ui.debug('imap', 'savemessage: new header is: %s: %s' % \
-                             (headername, headervalue))
-                content = self.savemessage_addheader(content, headername,
-                                                     headervalue)    
-            if len(content)>200:
-                dbg_output = "%s...%s" % (content[:150],
-                                          content[-50:])
-            else:
-                dbg_output = content
-            self.ui.debug('imap', "savemessage: date: %s, content: '%s'" %
-                          (date, dbg_output))
+            success = False # succeeded in APPENDING?
+            while not success:
+
+                # UIDPLUS extension provides us with an APPENDUID response.
+                use_uidplus = 'UIDPLUS' in imapobj.capabilities
+
+                # get the date of the message, so we can pass it to the server.
+                date = self.getmessageinternaldate(content, rtime)
+                content = re.sub("(?<!\r)\n", "\r\n", content)
+
+                if not use_uidplus:
+                    # insert a random unique header that we can fetch later
+                    (headername, headervalue) = self.generate_randomheader(
+                                                    content)
+                    self.ui.debug('imap', 'savemessage: header is: %s: %s' %\
+                                      (headername, headervalue))
+                    content = self.savemessage_addheader(content, headername,
+                                                         headervalue)    
+                if len(content)>200:
+                    dbg_output = "%s...%s" % (content[:150], content[-50:])
+                else:
+                    dbg_output = content
+                self.ui.debug('imap', "savemessage: date: %s, content: '%s'" %
+                             (date, dbg_output))
 
-            #Do the APPEND
-            try:
-                (typ, dat) = imapobj.append(self.getfullname(),
+                try:
+                    # Select folder for append and make the box READ-WRITE
+                    imapobj.select(self.getfullname())
+                except imapobj.readonly:
+                    # readonly exception. Return original uid to notify that
+                    # we did not save the message. (see savemessage in Base.py)
+                    self.ui.msgtoreadonly(self, uid, content, flags)
+                    return uid
+
+                #Do the APPEND
+                try:
+                    (typ, dat) = imapobj.append(self.getfullname(),
                                        imaputil.flagsmaildir2imap(flags),
                                        date, content)
-            except Exception, e:
-                # If the server responds with 'BAD', append() raise()s directly.
-                # So we need to prepare a response ourselves.
-                typ, dat = 'BAD', str(e)
-            if typ != 'OK': #APPEND failed
-                raise OfflineImapError("Saving msg in folder '%s', repository "
-                    "'%s' failed. Server reponded; %s %s\nMessage content was:"
-                    " %s" % (self, self.getrepository(), typ, dat, dbg_output),
-                                       OfflineImapError.ERROR.MESSAGE)
+                    success = True
+                except imapobj.abort, e:
+                    # connection has been reset, release connection and retry.
+                    self.ui.error(e, exc_info()[2])
+                    self.imapserver.releaseconnection(imapobj, True)
+                    imapobj = self.imapserver.acquireconnection()
+                except imapobj.error, e:
+                    # If the server responds with 'BAD', append() raise()s directly.
+                    # So we need to prepare a response ourselves.
+                    typ, dat = 'BAD', str(e)
+                    if typ != 'OK': #APPEND failed
+                        raise OfflineImapError("Saving msg in folder '%s', repository "
+                                               "'%s' failed. Server reponded; %s %s\nMessage content was:"
+                                               " %s" % (self, self.getrepository(), typ, dat, dbg_output),
+                                               OfflineImapError.ERROR.MESSAGE)
             # Checkpoint. Let it write out stuff, etc. Eg searches for
             # just uploaded messages won't work if we don't do this.
             (typ,dat) = imapobj.check()
-- 
1.7.4.1





More information about the OfflineIMAP-project mailing list