From 7bc45507cbcca54b35628152bc3d7b68127822f6 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Thu, 1 Sep 2011 11:00:58 +0200 Subject: [PATCH 1/3] Introduce new error level FOLDER_RETRY In cases where processing a folder failed, but a retry might well succeed e.g. when the server connection had simply timed out and was disconnected, we can throw a FOLDER_RETRY (which is less severe than FOLDER). Signed-off-by: Sebastian Spaeth Signed-off-by: Nicolas Sebrecht --- offlineimap/error.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/offlineimap/error.py b/offlineimap/error.py index 9e55162..aa7f535 100644 --- a/offlineimap/error.py +++ b/offlineimap/error.py @@ -5,11 +5,12 @@ class OfflineImapError(Exception): """Severity level of an Exception * **MESSAGE**: Abort the current message, but continue with folder + * **FOLDER_RETRY**: Error syncing folder, but do retry * **FOLDER**: Abort folder sync, but continue with next folder * **REPO**: Abort repository sync, continue with next account * **CRITICAL**: Immediately exit offlineimap """ - MESSAGE, FOLDER, REPO, CRITICAL = 0, 10, 20, 30 + MESSAGE, FOLDER_RETRY, FOLDER, REPO, CRITICAL = 0, 10, 15, 20, 30 def __init__(self, reason, severity, errcode=None): """ From 61fffd5c5f16abda5af003eba3692f698a17f489 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Thu, 1 Sep 2011 11:00:59 +0200 Subject: [PATCH 2/3] imaplibutil: Have SELECT throw an OfflineImapError when the connection died MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Imapserver.acquireconnection will succeed even whent the server connection has been terminated and the first IMAP operation will throw an exception. Often this is the folder SELECT operation (e.g. after an idle timeout), as has been reported by John Wiegley. Catch this case and throw an OfflineImapError with severity FOLDER_RETRY to notify consumers that they are supposed to retry. Signed-off-by: Sebastian Spaeth Signed-off-by: Nicolas Sebrecht --- offlineimap/imaplibutil.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/offlineimap/imaplibutil.py b/offlineimap/imaplibutil.py index fa3a303..529af6f 100644 --- a/offlineimap/imaplibutil.py +++ b/offlineimap/imaplibutil.py @@ -49,7 +49,14 @@ class UsefulIMAPMixIn: 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) + try: + result = self.__class__.__bases__[1].select(self, mailbox, readonly) + except self.abort, e: + # self.abort is raised when we are supposed to retry + errstr = "Server '%s' closed connection, error on SELECT '%s'. Ser"\ + "ver said: %s" % (self.host, mailbox, e.args[0]) + severity = OfflineImapError.ERROR.FOLDER_RETRY + raise OfflineImapError(errstr, severity) if result[0] != 'OK': #in case of error, bail out with OfflineImapError errstr = "Error SELECTing mailbox '%s', server reply:\n%s" %\ From f1555ec89311c1142f06a7085f9a097b0b2dccc8 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Thu, 1 Sep 2011 11:01:00 +0200 Subject: [PATCH 3/3] Catch terminated connections on the IDLE SELECT operation Handle the case gracefully where a server has closed an IMAP connection that we want to use for IDLEing. Simply have it dropped and get a new one in this case. THis should get rid of the errors reported by John Wiegley in mail id:"m2sjohd16t.fsf@gmail.com". Signed-off-by: Sebastian Spaeth Signed-off-by: Nicolas Sebrecht --- offlineimap/imapserver.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/offlineimap/imapserver.py b/offlineimap/imapserver.py index 3ce751c..e630f83 100644 --- a/offlineimap/imapserver.py +++ b/offlineimap/imapserver.py @@ -26,7 +26,7 @@ import socket import base64 import time import errno - +from sys import exc_info from socket import gaierror try: from ssl import SSLError, cert_time_to_seconds @@ -456,6 +456,7 @@ class IdleThread(object): self.parent = parent self.folder = folder self.event = Event() + self.ui = getglobalui() if folder is None: self.thread = Thread(target=self.noop) else: @@ -505,13 +506,24 @@ class IdleThread(object): self.imapaborted = True self.stop() - imapobj = self.parent.acquireconnection() - imapobj.select(self.folder) + success = False # successfully selected FOLDER? + while not success: + imapobj = self.parent.acquireconnection() + try: + imapobj.select(self.folder) + except OfflineImapError, e: + if e.severity == OfflineImapError.ERROR.FOLDER_RETRY: + # Connection closed, release connection and retry + self.ui.error(e, exc_info()[2]) + self.parent.releaseconnection(imapobj) + else: + raise e + else: + success = True if "IDLE" in imapobj.capabilities: imapobj.idle(callback=callback) else: - ui = getglobalui() - ui.warn("IMAP IDLE not supported on connection to %s." + self.ui.warn("IMAP IDLE not supported on connection to %s." "Falling back to old behavior: sleeping until next" "refresh cycle." %(imapobj.identifier,))