Reformat offlineimap/folder/IMAP.py

Add some spaces, remove lines,... now format is better (lintian).
This commit is contained in:
Rodolfo García Peñas (kix) 2020-08-29 19:43:09 +02:00
parent 2f97dda6d9
commit da1788db53

View File

@ -27,7 +27,6 @@ from offlineimap import imaputil, imaplibutil, emailutil, OfflineImapError
from offlineimap import globals from offlineimap import globals
from offlineimap.virtual_imaplib2 import MonthNames from offlineimap.virtual_imaplib2 import MonthNames
# Globals # Globals
CRLF = '\r\n' CRLF = '\r\n'
MSGCOPY_NAMESPACE = 'MSGCOPY_' MSGCOPY_NAMESPACE = 'MSGCOPY_'
@ -58,7 +57,7 @@ class IMAPFolder(BaseFolder):
self.visiblename = imaputil.decode_mailbox_name(self.visiblename) self.visiblename = imaputil.decode_mailbox_name(self.visiblename)
self.idle_mode = False self.idle_mode = False
self.expunge = repository.getexpunge() self.expunge = repository.getexpunge()
self.root = None # imapserver.root self.root = None # imapserver.root
self.imapserver = imapserver self.imapserver = imapserver
self.randomgenerator = random.Random() self.randomgenerator = random.Random()
# self.ui is set in BaseFolder. # self.ui is set in BaseFolder.
@ -103,7 +102,7 @@ class IMAPFolder(BaseFolder):
singlethreadperfolder_default = True singlethreadperfolder_default = True
onethread = self.config.getdefaultboolean( onethread = self.config.getdefaultboolean(
"Repository %s"% self.repository.getname(), "Repository %s" % self.repository.getname(),
"singlethreadperfolder", singlethreadperfolder_default) "singlethreadperfolder", singlethreadperfolder_default)
if onethread is True: if onethread is True:
return False return False
@ -114,8 +113,8 @@ class IMAPFolder(BaseFolder):
self.imapserver.connectionwait() self.imapserver.connectionwait()
def getmaxage(self): def getmaxage(self):
if self.config.getdefault("Account %s"% if self.config.getdefault("Account %s" %
self.accountname, "maxage", None): self.accountname, "maxage", None):
six.reraise(OfflineImapError, six.reraise(OfflineImapError,
OfflineImapError( OfflineImapError(
"maxage is not supported on IMAP-IMAP sync", "maxage is not supported on IMAP-IMAP sync",
@ -153,7 +152,7 @@ class IMAPFolder(BaseFolder):
# An IMAP folder has definitely changed if the number of # An IMAP folder has definitely changed if the number of
# messages or the UID of the last message have changed. Otherwise # messages or the UID of the last message have changed. Otherwise
# only flag changes could have occurred. # only flag changes could have occurred.
retry = True # Should we attempt another round or exit? retry = True # Should we attempt another round or exit?
while retry: while retry:
retry = False retry = False
imapobj = self.imapserver.acquireconnection() imapobj = self.imapserver.acquireconnection()
@ -213,14 +212,14 @@ class IMAPFolder(BaseFolder):
res_type, res_data = imapobj.search(None, search_conditions) res_type, res_data = imapobj.search(None, search_conditions)
if res_type != 'OK': if res_type != 'OK':
raise OfflineImapError("SEARCH in folder [%s]%s failed. " raise OfflineImapError("SEARCH in folder [%s]%s failed. "
"Search string was '%s'. Server responded '[%s] %s'"% ( "Search string was '%s'. Server responded '[%s] %s'" % (
self.getrepository(), self, search_cond, res_type, res_data), self.getrepository(), self, search_cond, res_type, res_data),
OfflineImapError.ERROR.FOLDER) OfflineImapError.ERROR.FOLDER)
except Exception as e: except Exception as e:
raise OfflineImapError("SEARCH in folder [%s]%s failed. " raise OfflineImapError("SEARCH in folder [%s]%s failed. "
"Search string was '%s'. Error: %s"% ( "Search string was '%s'. Error: %s" % (
self.getrepository(), self, search_cond, str(e)), self.getrepository(), self, search_cond, str(e)),
OfflineImapError.ERROR.FOLDER) OfflineImapError.ERROR.FOLDER)
# Davmail returns list instead of list of one element string. # Davmail returns list instead of list of one element string.
# On first run the first element is empty. # On first run the first element is empty.
if ' ' in res_data[0] or res_data[0] == '': if ' ' in res_data[0] or res_data[0] == '':
@ -239,20 +238,20 @@ class IMAPFolder(BaseFolder):
conditions = [] conditions = []
# 1. min_uid condition. # 1. min_uid condition.
if min_uid != None: if min_uid != None:
conditions.append("UID %d:*"% min_uid) conditions.append("UID %d:*" % min_uid)
# 2. date condition. # 2. date condition.
elif min_date != None: elif min_date != None:
# Find out what the oldest message is that we should look at. # Find out what the oldest message is that we should look at.
conditions.append("SINCE %02d-%s-%d"% ( conditions.append("SINCE %02d-%s-%d" % (
min_date[2], MonthNames[min_date[1]], min_date[0])) min_date[2], MonthNames[min_date[1]], min_date[0]))
# 3. maxsize condition. # 3. maxsize condition.
maxsize = self.getmaxsize() maxsize = self.getmaxsize()
if maxsize != None: if maxsize != None:
conditions.append("SMALLER %d"% maxsize) conditions.append("SMALLER %d" % maxsize)
if len(conditions) >= 1: if len(conditions) >= 1:
# Build SEARCH command. # Build SEARCH command.
search_cond = "(%s)"% ' '.join(conditions) search_cond = "(%s)" % ' '.join(conditions)
search_result = search(search_cond) search_result = search(search_cond)
return imaputil.uid_sequence(search_result) return imaputil.uid_sequence(search_result)
@ -263,7 +262,6 @@ class IMAPFolder(BaseFolder):
def msglist_item_initializer(self, uid): def msglist_item_initializer(self, uid):
return {'uid': uid, 'flags': set(), 'time': 0} return {'uid': uid, 'flags': set(), 'time': 0}
# Interface from BaseFolder # Interface from BaseFolder
def cachemessagelist(self, min_date=None, min_uid=None): def cachemessagelist(self, min_date=None, min_uid=None):
self.ui.loadmessagelist(self.repository, self) self.ui.loadmessagelist(self.repository, self)
@ -274,19 +272,20 @@ class IMAPFolder(BaseFolder):
msgsToFetch = self._msgs_to_fetch( msgsToFetch = self._msgs_to_fetch(
imapobj, min_date=min_date, min_uid=min_uid) imapobj, min_date=min_date, min_uid=min_uid)
if not msgsToFetch: if not msgsToFetch:
return # No messages to sync. return # No messages to sync.
# Get the flags and UIDs for these. single-quotes prevent # Get the flags and UIDs for these. single-quotes prevent
# imaplib2 from quoting the sequence. # imaplib2 from quoting the sequence.
fetch_msg = "%s"% msgsToFetch fetch_msg = "%s" % msgsToFetch
self.ui.debug('imap', "calling imaplib2 fetch command: %s %s"% self.ui.debug('imap', "calling imaplib2 fetch command: %s %s" %
(fetch_msg, '(FLAGS UID INTERNALDATE)')) (fetch_msg, '(FLAGS UID INTERNALDATE)'))
res_type, response = imapobj.fetch( res_type, response = imapobj.fetch(
fetch_msg, '(FLAGS UID INTERNALDATE)') fetch_msg, '(FLAGS UID INTERNALDATE)')
if res_type != 'OK': if res_type != 'OK':
raise OfflineImapError("FETCHING UIDs in folder [%s]%s failed. " raise OfflineImapError("FETCHING UIDs in folder [%s]%s failed. "
"Server responded '[%s] %s'"% (self.getrepository(), self, "Server responded '[%s] %s'" % (self.getrepository(), self,
res_type, response), OfflineImapError.ERROR.FOLDER) res_type, response),
OfflineImapError.ERROR.FOLDER)
finally: finally:
self.imapserver.releaseconnection(imapobj) self.imapserver.releaseconnection(imapobj)
@ -298,8 +297,8 @@ class IMAPFolder(BaseFolder):
messagestr = messagestr.decode('utf-8').split(' ', 1)[1] messagestr = messagestr.decode('utf-8').split(' ', 1)[1]
options = imaputil.flags2hash(messagestr) options = imaputil.flags2hash(messagestr)
if 'UID' not in options: if 'UID' not in options:
self.ui.warn('No UID in message with options %s'% self.ui.warn('No UID in message with options %s' %
str(options), minor=1) str(options), minor=1)
else: else:
uid = int(options['UID']) uid = int(options['UID'])
self.messagelist[uid] = self.msglist_item_initializer(uid) self.messagelist[uid] = self.msglist_item_initializer(uid)
@ -307,7 +306,7 @@ class IMAPFolder(BaseFolder):
keywords = imaputil.flagsimap2keywords(options['FLAGS']) keywords = imaputil.flagsimap2keywords(options['FLAGS'])
rtime = imaplibutil.Internaldate2epoch(messagestr) rtime = imaplibutil.Internaldate2epoch(messagestr)
self.messagelist[uid] = {'uid': uid, 'flags': flags, 'time': rtime, self.messagelist[uid] = {'uid': uid, 'flags': flags, 'time': rtime,
'keywords': keywords} 'keywords': keywords}
self.ui.messagelistloaded(self.repository, self, self.getmessagecount()) self.ui.messagelistloaded(self.repository, self, self.getmessagecount())
# Interface from BaseFolder # Interface from BaseFolder
@ -330,11 +329,11 @@ class IMAPFolder(BaseFolder):
data = data[0][1].replace(CRLF, "\n") data = data[0][1].replace(CRLF, "\n")
if len(data) > 200: if len(data) > 200:
dbg_output = "%s...%s"% (str(data)[:150], str(data)[-50:]) dbg_output = "%s...%s" % (str(data)[:150], str(data)[-50:])
else: else:
dbg_output = data dbg_output = data
self.ui.debug('imap', "Returned object from fetching %d: '%s'"% self.ui.debug('imap', "Returned object from fetching %d: '%s'" %
(uid, dbg_output)) (uid, dbg_output))
return data return data
@ -374,40 +373,39 @@ class IMAPFolder(BaseFolder):
# Compute unsigned crc32 of 'content' as unique hash. # Compute unsigned crc32 of 'content' as unique hash.
# NB: crc32 returns unsigned only starting with python 3.0. # NB: crc32 returns unsigned only starting with python 3.0.
headervalue = str( binascii.crc32(content) & 0xffffffff ) + '-' headervalue = str(binascii.crc32(content) & 0xffffffff) + '-'
headervalue += str(self.randomgenerator.randint(0,9999999999)) headervalue += str(self.randomgenerator.randint(0, 9999999999))
return (headername, headervalue) return (headername, headervalue)
def __savemessage_searchforheader(self, imapobj, headername, headervalue): def __savemessage_searchforheader(self, imapobj, headername, headervalue):
self.ui.debug('imap', '__savemessage_searchforheader called for %s: %s'% self.ui.debug('imap', '__savemessage_searchforheader called for %s: %s' %
(headername, headervalue)) (headername, headervalue))
# Now find the UID it got. # Now find the UID it got.
headervalue = imapobj._quote(headervalue) headervalue = imapobj._quote(headervalue)
try: try:
matchinguids = imapobj.uid('search', 'HEADER', matchinguids = imapobj.uid('search', 'HEADER',
headername, headervalue)[1][0] headername, headervalue)[1][0]
except imapobj.error as err: except imapobj.error as err:
# IMAP server doesn't implement search or had a problem. # IMAP server doesn't implement search or had a problem.
self.ui.debug('imap', "__savemessage_searchforheader: got IMAP " self.ui.debug('imap', "__savemessage_searchforheader: got IMAP "
"error '%s' while attempting to UID SEARCH for message with " "error '%s' while attempting to UID SEARCH for message with "
"header %s"% (err, headername)) "header %s" % (err, headername))
return 0 return 0
self.ui.debug('imap', "__savemessage_searchforheader got initial " self.ui.debug('imap', "__savemessage_searchforheader got initial "
"matchinguids: " + repr(matchinguids)) "matchinguids: " + repr(matchinguids))
if matchinguids == '': if matchinguids == '':
self.ui.debug('imap', "__savemessage_searchforheader: UID SEARCH " self.ui.debug('imap', "__savemessage_searchforheader: UID SEARCH "
"for message with header %s yielded no results"% headername) "for message with header %s yielded no results" % headername)
return 0 return 0
matchinguids = matchinguids.split(' ') matchinguids = matchinguids.split(' ')
self.ui.debug('imap', '__savemessage_searchforheader: matchinguids now ' self.ui.debug('imap', '__savemessage_searchforheader: matchinguids now '
+ repr(matchinguids)) + repr(matchinguids))
if len(matchinguids) != 1 or matchinguids[0] is None: if len(matchinguids) != 1 or matchinguids[0] is None:
raise OfflineImapError( raise OfflineImapError(
"While attempting to find UID for message with " "While attempting to find UID for message with "
"header %s, got wrong-sized matchinguids of %s"% "header %s, got wrong-sized matchinguids of %s" %
(headername, str(matchinguids)), (headername, str(matchinguids)),
OfflineImapError.ERROR.MESSAGE OfflineImapError.ERROR.MESSAGE
) )
@ -436,8 +434,8 @@ class IMAPFolder(BaseFolder):
Returns UID when found, 0 when not found.""" Returns UID when found, 0 when not found."""
self.ui.debug('imap', '__savemessage_fetchheaders called for %s: %s'% \ self.ui.debug('imap', '__savemessage_fetchheaders called for %s: %s' % \
(headername, headervalue)) (headername, headervalue))
# Run "fetch X:* rfc822.header". # Run "fetch X:* rfc822.header".
# Since we stored the mail we are looking for just recently, it would # Since we stored the mail we are looking for just recently, it would
@ -456,10 +454,10 @@ class IMAPFolder(BaseFolder):
# with the range X:*. So we use bytearray to stop imaplib from getting # with the range X:*. So we use bytearray to stop imaplib from getting
# in our way. # in our way.
result = imapobj.uid('FETCH', bytearray('%d:*'% start), 'rfc822.header') result = imapobj.uid('FETCH', bytearray('%d:*' % start), 'rfc822.header')
if result[0] != 'OK': if result[0] != 'OK':
raise OfflineImapError('Error fetching mail headers: %s'% raise OfflineImapError('Error fetching mail headers: %s' %
'. '.join(result[1]), OfflineImapError.ERROR.MESSAGE) '. '.join(result[1]), OfflineImapError.ERROR.MESSAGE)
# result is like: # result is like:
# [ # [
@ -474,8 +472,8 @@ class IMAPFolder(BaseFolder):
for item in result: for item in result:
if found is None and type(item) == tuple: if found is None and type(item) == tuple:
# Walk just tuples. # Walk just tuples.
if re.search("(?:^|\\r|\\n)%s:\s*%s(?:\\r|\\n)"% (headername, headervalue), if re.search("(?:^|\\r|\\n)%s:\s*%s(?:\\r|\\n)" % (headername, headervalue),
item[1], flags=re.IGNORECASE): item[1], flags=re.IGNORECASE):
found = item[0] found = item[0]
elif found is not None: elif found is not None:
if type(item) == type(""): if type(item) == type(""):
@ -493,14 +491,14 @@ class IMAPFolder(BaseFolder):
if uid: if uid:
return int(uid.group(1)) return int(uid.group(1))
self.ui.warn("Can't parse FETCH response, can't find UID in %s"% self.ui.warn("Can't parse FETCH response, can't find UID in %s" %
item item
) )
self.ui.debug('imap', "Got: %s"% repr(result)) self.ui.debug('imap', "Got: %s" % repr(result))
else: else:
self.ui.warn("Can't parse FETCH response, we awaited string: %s"% self.ui.warn("Can't parse FETCH response, we awaited string: %s" %
repr(item) repr(item)
) )
return 0 return 0
@ -557,13 +555,13 @@ class IMAPFolder(BaseFolder):
# will rause a ValueError if the year is 0102 but not 1902, # will rause a ValueError if the year is 0102 but not 1902,
# but some IMAP servers nonetheless choke on 1902. # but some IMAP servers nonetheless choke on 1902.
self.ui.debug('imap', "Message with invalid date %s. " self.ui.debug('imap', "Message with invalid date %s. "
"Server will use local time."% datetuple) "Server will use local time." % datetuple)
return None return None
# Produce a string representation of datetuple that works as # Produce a string representation of datetuple that works as
# INTERNALDATE. # INTERNALDATE.
num2mon = {1:'Jan', 2:'Feb', 3:'Mar', 4:'Apr', 5:'May', 6:'Jun', num2mon = {1: 'Jan', 2: 'Feb', 3: 'Mar', 4: 'Apr', 5: 'May', 6: 'Jun',
7:'Jul', 8:'Aug', 9:'Sep', 10:'Oct', 11:'Nov', 12:'Dec'} 7: 'Jul', 8: 'Aug', 9: 'Sep', 10: 'Oct', 11: 'Nov', 12: 'Dec'}
# tm_isdst coming from email.parsedate is not usable, we still use it # tm_isdst coming from email.parsedate is not usable, we still use it
# here, mhh. # here, mhh.
@ -571,11 +569,11 @@ class IMAPFolder(BaseFolder):
zone = -time.altzone zone = -time.altzone
else: else:
zone = -time.timezone zone = -time.timezone
offset_h, offset_m = divmod(zone//60, 60) offset_h, offset_m = divmod(zone // 60, 60)
internaldate = '"%02d-%s-%04d %02d:%02d:%02d %+03d%02d"'% \ internaldate = '"%02d-%s-%04d %02d:%02d:%02d %+03d%02d"' % \
(datetuple.tm_mday, num2mon[datetuple.tm_mon], datetuple.tm_year, \ (datetuple.tm_mday, num2mon[datetuple.tm_mon], datetuple.tm_year, \
datetuple.tm_hour, datetuple.tm_min, datetuple.tm_sec, offset_h, offset_m) datetuple.tm_hour, datetuple.tm_min, datetuple.tm_sec, offset_h, offset_m)
return internaldate return internaldate
@ -618,7 +616,7 @@ class IMAPFolder(BaseFolder):
if not msg_id: if not msg_id:
msg_id = '[unknown message-id]' msg_id = '[unknown message-id]'
retry_left = 2 # succeeded in APPENDING? retry_left = 2 # succeeded in APPENDING?
imapobj = self.imapserver.acquireconnection() imapobj = self.imapserver.acquireconnection()
# NB: in the finally clause for this try we will release # NB: in the finally clause for this try we will release
# NB: the acquired imapobj, so don't do that twice unless # NB: the acquired imapobj, so don't do that twice unless
@ -635,16 +633,16 @@ class IMAPFolder(BaseFolder):
# Insert a random unique header that we can fetch later. # Insert a random unique header that we can fetch later.
(headername, headervalue) = self.__generate_randomheader( (headername, headervalue) = self.__generate_randomheader(
content) content)
self.ui.debug('imap', 'savemessage: header is: %s: %s'% self.ui.debug('imap', 'savemessage: header is: %s: %s' %
(headername, headervalue)) (headername, headervalue))
content = self.addmessageheader(content, CRLF, headername, headervalue) content = self.addmessageheader(content, CRLF, headername, headervalue)
if len(content) > 200: if len(content) > 200:
dbg_output = "%s...%s"% (content[:150], content[-50:]) dbg_output = "%s...%s" % (content[:150], content[-50:])
else: else:
dbg_output = content dbg_output = content
self.ui.debug('imap', "savemessage: date: %s, content: '%s'"% self.ui.debug('imap', "savemessage: date: %s, content: '%s'" %
(date, dbg_output)) (date, dbg_output))
try: try:
# Select folder for append and make the box READ-WRITE. # Select folder for append and make the box READ-WRITE.
@ -658,7 +656,7 @@ class IMAPFolder(BaseFolder):
# Do the APPEND. # Do the APPEND.
try: try:
(typ, dat) = imapobj.append(self.getfullIMAPname(), (typ, dat) = imapobj.append(self.getfullIMAPname(),
imaputil.flagsmaildir2imap(flags), date, content) imaputil.flagsmaildir2imap(flags), date, content)
# This should only catch 'NO' responses since append() # This should only catch 'NO' responses since append()
# will raise an exception for 'BAD' responses: # will raise an exception for 'BAD' responses:
if typ != 'OK': if typ != 'OK':
@ -670,10 +668,10 @@ class IMAPFolder(BaseFolder):
# and continue with the next account. # and continue with the next account.
msg = \ msg = \
"Saving msg (%s) in folder '%s', repository '%s' failed (abort). " \ "Saving msg (%s) in folder '%s', repository '%s' failed (abort). " \
"Server responded: %s %s\n"% \ "Server responded: %s %s\n" % \
(msg_id, self, self.getrepository(), typ, dat) (msg_id, self, self.getrepository(), typ, dat)
raise OfflineImapError(msg, OfflineImapError.ERROR.REPO) raise OfflineImapError(msg, OfflineImapError.ERROR.REPO)
retry_left = 0 # Mark as success. retry_left = 0 # Mark as success.
except imapobj.abort as e: except imapobj.abort as e:
# Connection has been reset, release connection and retry. # Connection has been reset, release connection and retry.
retry_left -= 1 retry_left -= 1
@ -682,14 +680,14 @@ class IMAPFolder(BaseFolder):
if not retry_left: if not retry_left:
six.reraise(OfflineImapError, six.reraise(OfflineImapError,
OfflineImapError("Saving msg (%s) in folder '%s', " OfflineImapError("Saving msg (%s) in folder '%s', "
"repository '%s' failed (abort). Server responded: %s\n" "repository '%s' failed (abort). Server responded: %s\n"
"Message content was: %s"% "Message content was: %s" %
(msg_id, self, self.getrepository(), str(e), dbg_output), (msg_id, self, self.getrepository(), str(e), dbg_output),
OfflineImapError.ERROR.MESSAGE), OfflineImapError.ERROR.MESSAGE),
exc_info()[2]) exc_info()[2])
# XXX: is this still needed? # XXX: is this still needed?
self.ui.error(e, exc_info()[2]) self.ui.error(e, exc_info()[2])
except imapobj.error as e: # APPEND failed except imapobj.error as e: # APPEND failed
# If the server responds with 'BAD', append() # If the server responds with 'BAD', append()
# raise()s directly. So we catch that too. # raise()s directly. So we catch that too.
# drop conn, it might be bad. # drop conn, it might be bad.
@ -697,14 +695,14 @@ class IMAPFolder(BaseFolder):
imapobj = None imapobj = None
six.reraise(OfflineImapError, six.reraise(OfflineImapError,
OfflineImapError("Saving msg (%s) folder '%s', repo '%s'" OfflineImapError("Saving msg (%s) folder '%s', repo '%s'"
"failed (error). Server responded: %s\nMessage content was: " "failed (error). Server responded: %s\nMessage content was: "
"%s"% (msg_id, self, self.getrepository(), str(e), dbg_output), "%s" % (msg_id, self, self.getrepository(), str(e), dbg_output),
OfflineImapError.ERROR.MESSAGE), OfflineImapError.ERROR.MESSAGE),
exc_info()[2]) exc_info()[2])
# 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()
assert(typ == 'OK') assert (typ == 'OK')
# Get the new UID, do we use UIDPLUS? # Get the new UID, do we use UIDPLUS?
if use_uidplus: if use_uidplus:
@ -717,55 +715,54 @@ class IMAPFolder(BaseFolder):
resp = imapobj._get_untagged_response('APPENDUID') resp = imapobj._get_untagged_response('APPENDUID')
if resp == [None] or resp is None: if resp == [None] or resp is None:
self.ui.warn("Server supports UIDPLUS but got no APPENDUID " self.ui.warn("Server supports UIDPLUS but got no APPENDUID "
"appending a message. Got: %s."% str(resp)) "appending a message. Got: %s." % str(resp))
return 0 return 0
try: try:
uid = int(resp[-1].split(' ')[1]) uid = int(resp[-1].split(' ')[1])
except ValueError as e: except ValueError as e:
uid = 0 # Definetly not what we should have. uid = 0 # Definetly not what we should have.
except Exception as e: except Exception as e:
raise OfflineImapError("Unexpected response: %s"% str(resp), raise OfflineImapError("Unexpected response: %s" % str(resp),
OfflineImapError.ERROR.MESSAGE) OfflineImapError.ERROR.MESSAGE)
if uid == 0: if uid == 0:
self.ui.warn("savemessage: Server supports UIDPLUS, but" self.ui.warn("savemessage: Server supports UIDPLUS, but"
" we got no usable UID back. APPENDUID reponse was " " we got no usable UID back. APPENDUID reponse was "
"'%s'"% str(resp)) "'%s'" % str(resp))
else: else:
try: try:
# We don't use UIDPLUS. # We don't use UIDPLUS.
uid = self.__savemessage_searchforheader(imapobj, headername, uid = self.__savemessage_searchforheader(imapobj, headername,
headervalue) headervalue)
# See docs for savemessage in Base.py for explanation # See docs for savemessage in Base.py for explanation
# of this and other return values. # of this and other return values.
if uid == 0: if uid == 0:
self.ui.debug('imap', 'savemessage: attempt to get new UID ' self.ui.debug('imap', 'savemessage: attempt to get new UID '
'UID failed. Search headers manually.') 'UID failed. Search headers manually.')
uid = self.__savemessage_fetchheaders(imapobj, headername, uid = self.__savemessage_fetchheaders(imapobj, headername,
headervalue) headervalue)
self.ui.warn("savemessage: Searching mails for new " self.ui.warn("savemessage: Searching mails for new "
"Message-ID failed. Could not determine new UID " "Message-ID failed. Could not determine new UID "
"on %s."% self.getname()) "on %s." % self.getname())
# Something wrong happened while trying to get the UID. Explain # Something wrong happened while trying to get the UID. Explain
# the error might be about the 'get UID' process not necesseraly # the error might be about the 'get UID' process not necesseraly
# the APPEND. # the APPEND.
except Exception: except Exception:
self.ui.warn("%s: could not determine the UID while we got " self.ui.warn("%s: could not determine the UID while we got "
"no error while appending the email with '%s: %s'"% "no error while appending the email with '%s: %s'" %
(self.getname(), headername, headervalue) (self.getname(), headername, headervalue)
) )
raise raise
finally: finally:
if imapobj: if imapobj:
self.imapserver.releaseconnection(imapobj) self.imapserver.releaseconnection(imapobj)
if uid: # Avoid UID FETCH 0 crash happening later on. if uid: # Avoid UID FETCH 0 crash happening later on.
self.messagelist[uid] = self.msglist_item_initializer(uid) self.messagelist[uid] = self.msglist_item_initializer(uid)
self.messagelist[uid]['flags'] = flags self.messagelist[uid]['flags'] = flags
self.ui.debug('imap', 'savemessage: returning new UID %d'% uid) self.ui.debug('imap', 'savemessage: returning new UID %d' % uid)
return uid return uid
def _fetch_from_imap(self, uids, retry_num=1): def _fetch_from_imap(self, uids, retry_num=1):
"""Fetches data from IMAP server. """Fetches data from IMAP server.
@ -777,7 +774,7 @@ class IMAPFolder(BaseFolder):
imapobj = self.imapserver.acquireconnection() imapobj = self.imapserver.acquireconnection()
try: try:
query = "(%s)"% (" ".join(self.imap_query)) query = "(%s)" % (" ".join(self.imap_query))
fails_left = retry_num # Retry on dropped connection. fails_left = retry_num # Retry on dropped connection.
while fails_left: while fails_left:
try: try:
@ -789,24 +786,24 @@ class IMAPFolder(BaseFolder):
# self.ui.error() will show the original traceback. # self.ui.error() will show the original traceback.
if fails_left <= 0: if fails_left <= 0:
message = ("%s, while fetching msg %r in folder %r." message = ("%s, while fetching msg %r in folder %r."
" Max retry reached (%d)"% " Max retry reached (%d)" %
(e, uids, self.name, retry_num)) (e, uids, self.name, retry_num))
severity = OfflineImapError.ERROR.MESSAGE severity = OfflineImapError.ERROR.MESSAGE
raise OfflineImapError(message, raise OfflineImapError(message,
OfflineImapError.ERROR.MESSAGE) OfflineImapError.ERROR.MESSAGE)
self.ui.error("%s. While fetching msg %r in folder %r." self.ui.error("%s. While fetching msg %r in folder %r."
" Query: %s Retrying (%d/%d)"% ( " Query: %s Retrying (%d/%d)" % (
e, uids, self.name, query, e, uids, self.name, query,
retry_num - fails_left, retry_num retry_num - fails_left, retry_num
) )
) )
# Release dropped connection, and get a new one. # Release dropped connection, and get a new one.
self.imapserver.releaseconnection(imapobj, True) self.imapserver.releaseconnection(imapobj, True)
imapobj = self.imapserver.acquireconnection() imapobj = self.imapserver.acquireconnection()
finally: finally:
# The imapobj here might be different than the one created before # The imapobj here might be different than the one created before
# the ``try`` clause. So please avoid transforming this to a nice # the ``try`` clause. So please avoid transforming this to a nice
# ``with`` without taking this into account. # ``with`` without taking this into account.
self.imapserver.releaseconnection(imapobj) self.imapserver.releaseconnection(imapobj)
# Ensure to not consider unsolicited FETCH responses caused by flag # Ensure to not consider unsolicited FETCH responses caused by flag
@ -820,13 +817,13 @@ class IMAPFolder(BaseFolder):
# data for the UID FETCH command. # data for the UID FETCH command.
if data == [None] or res_type != 'OK' or len(data) != 1: if data == [None] or res_type != 'OK' or len(data) != 1:
severity = OfflineImapError.ERROR.MESSAGE severity = OfflineImapError.ERROR.MESSAGE
reason = "IMAP server '%s' failed to fetch messages UID '%s'."\ reason = "IMAP server '%s' failed to fetch messages UID '%s'." \
" Server responded: %s %s"% (self.getrepository(), uids, " Server responded: %s %s" % (self.getrepository(), uids,
res_type, data) res_type, data)
if data == [None] or len(data) < 1: if data == [None] or len(data) < 1:
# IMAP server did not find a message with this UID. # IMAP server did not find a message with this UID.
reason = "IMAP server '%s' does not have a message "\ reason = "IMAP server '%s' does not have a message " \
"with UID '%s'"% (self.getrepository(), uids) "with UID '%s'" % (self.getrepository(), uids)
raise OfflineImapError(reason, severity) raise OfflineImapError(reason, severity)
# Convert bytes to str # Convert bytes to str
@ -836,7 +833,6 @@ class IMAPFolder(BaseFolder):
return ndata return ndata
def _store_to_imap(self, imapobj, uid, field, data): def _store_to_imap(self, imapobj, uid, field, data):
"""Stores data to IMAP server """Stores data to IMAP server
@ -850,9 +846,9 @@ class IMAPFolder(BaseFolder):
res_type, retdata = imapobj.uid('store', uid, field, data) res_type, retdata = imapobj.uid('store', uid, field, data)
if res_type != 'OK': if res_type != 'OK':
severity = OfflineImapError.ERROR.MESSAGE severity = OfflineImapError.ERROR.MESSAGE
reason = "IMAP server '%s' failed to store %s for message UID '%d'."\ reason = "IMAP server '%s' failed to store %s for message UID '%d'." \
"Server responded: %s %s"% ( "Server responded: %s %s" % (
self.getrepository(), field, uid, res_type, retdata) self.getrepository(), field, uid, res_type, retdata)
raise OfflineImapError(reason, severity) raise OfflineImapError(reason, severity)
return retdata[0] return retdata[0]
@ -867,7 +863,7 @@ class IMAPFolder(BaseFolder):
imapobj = self.imapserver.acquireconnection() imapobj = self.imapserver.acquireconnection()
try: try:
result = self._store_to_imap(imapobj, str(uid), 'FLAGS', result = self._store_to_imap(imapobj, str(uid), 'FLAGS',
imaputil.flagsmaildir2imap(flags)) imaputil.flagsmaildir2imap(flags))
except imapobj.readonly: except imapobj.readonly:
self.ui.flagstoreadonly(self, [uid], flags) self.ui.flagstoreadonly(self, [uid], flags)
return return
@ -912,11 +908,11 @@ class IMAPFolder(BaseFolder):
self.ui.flagstoreadonly(self, uidlist, flags) self.ui.flagstoreadonly(self, uidlist, flags)
return return
response = imapobj.uid('store', response = imapobj.uid('store',
imaputil.uid_sequence(uidlist), operation + 'FLAGS', imaputil.uid_sequence(uidlist), operation + 'FLAGS',
imaputil.flagsmaildir2imap(flags)) imaputil.flagsmaildir2imap(flags))
if response[0] != 'OK': if response[0] != 'OK':
raise OfflineImapError( raise OfflineImapError(
'Error with store: %s'% '. '.join(response[1]), 'Error with store: %s' % '. '.join(response[1]),
OfflineImapError.ERROR.MESSAGE) OfflineImapError.ERROR.MESSAGE)
response = response[1] response = response[1]
finally: finally:
@ -947,16 +943,14 @@ class IMAPFolder(BaseFolder):
elif operation == '-': elif operation == '-':
self.messagelist[uid]['flags'] -= flags self.messagelist[uid]['flags'] -= flags
def __processmessagesflags(self, operation, uidlist, flags): def __processmessagesflags(self, operation, uidlist, flags):
# Hack for those IMAP servers with a limited line length. # Hack for those IMAP servers with a limited line length.
batch_size = 100 batch_size = 100
for i in range(0, len(uidlist), batch_size): for i in range(0, len(uidlist), batch_size):
self.__processmessagesflags_real(operation, self.__processmessagesflags_real(operation,
uidlist[i:i + batch_size], flags) uidlist[i:i + batch_size], flags)
return return
# Interface from BaseFolder # Interface from BaseFolder
def change_message_uid(self, uid, new_uid): def change_message_uid(self, uid, new_uid):
"""Change the message from existing uid to new_uid """Change the message from existing uid to new_uid
@ -964,7 +958,7 @@ class IMAPFolder(BaseFolder):
If the backend supports it. IMAP does not and will throw errors.""" If the backend supports it. IMAP does not and will throw errors."""
raise OfflineImapError('IMAP backend cannot change a messages UID from ' raise OfflineImapError('IMAP backend cannot change a messages UID from '
'%d to %d'% (uid, new_uid), OfflineImapError.ERROR.MESSAGE) '%d to %d' % (uid, new_uid), OfflineImapError.ERROR.MESSAGE)
# Interface from BaseFolder # Interface from BaseFolder
def deletemessage(self, uid): def deletemessage(self, uid):
@ -987,7 +981,7 @@ class IMAPFolder(BaseFolder):
self.ui.deletereadonly(self, uidlist) self.ui.deletereadonly(self, uidlist)
return return
if self.expunge: if self.expunge:
assert(imapobj.expunge()[0] == 'OK') assert (imapobj.expunge()[0] == 'OK')
finally: finally:
self.imapserver.releaseconnection(imapobj) self.imapserver.releaseconnection(imapobj)
for uid in uidlist: for uid in uidlist: