Error proof IMAP.APPEND against dropped connections

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@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
Sebastian Spaeth 2011-09-06 13:19:26 +02:00 committed by Nicolas Sebrecht
parent 9c678c9d6b
commit 2cf6155282

View File

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