From 7df765cfdb4096a33f35bf709db6820a6c2c79fd Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Sun, 3 Aug 2014 16:47:26 +0400 Subject: [PATCH] Properly manipulate contents of messagelist for folder Create initializer function that puts default values to all fields of message list item. Fix all code that directly assigns some hash to the elements of messagelist: for direct assignments only initializer is now permitted, all other modification are done in-place. Signed-off-by: Eygene Ryabinkin --- offlineimap/folder/Base.py | 11 +++++++++++ offlineimap/folder/Gmail.py | 6 ++++++ offlineimap/folder/GmailMaildir.py | 6 ++++++ offlineimap/folder/IMAP.py | 8 +++++++- offlineimap/folder/LocalStatus.py | 19 ++++++++++++++++--- offlineimap/folder/LocalStatusSQLite.py | 16 ++++++++++++++-- offlineimap/folder/Maildir.py | 17 +++++++++++++++-- 7 files changed, 75 insertions(+), 8 deletions(-) diff --git a/offlineimap/folder/Base.py b/offlineimap/folder/Base.py index 41fd627..b56cd1b 100644 --- a/offlineimap/folder/Base.py +++ b/offlineimap/folder/Base.py @@ -236,6 +236,17 @@ class BaseFolder(object): You must call cachemessagelist() before calling this function!""" raise NotImplementedError + def msglist_item_initializer(self, uid): + """ + Returns value for empty messagelist element with given UID. + + This function must initialize all fields of messagelist item + and must be called every time when one creates new messagelist + entry to ensure that all fields that must be present are present. + + """ + raise NotImplementedError + def uidexists(self, uid): """Returns True if uid exists""" return uid in self.getmessagelist() diff --git a/offlineimap/folder/Gmail.py b/offlineimap/folder/Gmail.py index 2a598de..e3ef92a 100644 --- a/offlineimap/folder/Gmail.py +++ b/offlineimap/folder/Gmail.py @@ -110,6 +110,11 @@ class GmailFolder(IMAPFolder): else: return set() + # Interface from BaseFolder + def msglist_item_initializer(self, uid): + return {'uid': uid, 'flags': set(), 'labels': set(), 'time': 0} + + # TODO: merge this code with the parent's cachemessagelist: # TODO: they have too much common logics. def cachemessagelist(self): @@ -152,6 +157,7 @@ class GmailFolder(IMAPFolder): minor = 1) else: uid = long(options['UID']) + self.messagelist[uid] = self.msglist_item_initializer(uid) flags = imaputil.flagsimap2maildir(options['FLAGS']) m = re.search('\(([^\)]*)\)', options['X-GM-LABELS']) if m: diff --git a/offlineimap/folder/GmailMaildir.py b/offlineimap/folder/GmailMaildir.py index be0d20d..b62b701 100644 --- a/offlineimap/folder/GmailMaildir.py +++ b/offlineimap/folder/GmailMaildir.py @@ -56,6 +56,12 @@ class GmailMaildirFolder(MaildirFolder): return True return False #Nope, nothing changed + + # Interface from BaseFolder + def msglist_item_initializer(self, uid): + return {'flags': set(), 'labels': set(), 'filename': '/no-dir/no-such-file/', 'mtime': 0} + + def cachemessagelist(self): if self.messagelist is None: self.messagelist = self._scanfolder() diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py index e317baf..a698888 100644 --- a/offlineimap/folder/IMAP.py +++ b/offlineimap/folder/IMAP.py @@ -202,6 +202,10 @@ class IMAPFolder(BaseFolder): return msgsToFetch + # Interface from BaseFolder + def msglist_item_initializer(self, uid): + return {'uid': uid, 'flags': set(), 'time': 0} + # Interface from BaseFolder def cachemessagelist(self): @@ -239,6 +243,7 @@ class IMAPFolder(BaseFolder): minor = 1) else: uid = long(options['UID']) + self.messagelist[uid] = self.msglist_item_initializer(uid) flags = imaputil.flagsimap2maildir(options['FLAGS']) rtime = imaplibutil.Internaldate2epoch(messagestr) self.messagelist[uid] = {'uid': uid, 'flags': flags, 'time': rtime} @@ -641,7 +646,8 @@ class IMAPFolder(BaseFolder): if imapobj: self.imapserver.releaseconnection(imapobj) if uid: # avoid UID FETCH 0 crash happening later on - self.messagelist[uid] = {'uid': uid, 'flags': flags} + self.messagelist[uid] = self.msglist_item_initializer(uid) + self.messagelist[uid]['flags'] = flags self.ui.debug('imap', 'savemessage: returning new UID %d' % uid) return uid diff --git a/offlineimap/folder/LocalStatus.py b/offlineimap/folder/LocalStatus.py index 7e5928e..1dccf90 100644 --- a/offlineimap/folder/LocalStatus.py +++ b/offlineimap/folder/LocalStatus.py @@ -57,6 +57,11 @@ class LocalStatusFolder(BaseFolder): os.unlink(self.filename) + # Interface from BaseFolder + def msglist_item_initializer(self, uid): + return {'uid': uid, 'flags': set(), 'labels': set(), 'time': 0, 'mtime': 0} + + def readstatus_v1(self, fp): """ Read status folder in format version 1. @@ -76,7 +81,8 @@ class LocalStatusFolder(BaseFolder): (line, self.filename) self.ui.warn(errstr) raise ValueError(errstr) - self.messagelist[uid] = {'uid': uid, 'flags': flags, 'mtime': 0, 'labels': set()} + self.messagelist[uid] = self.msglist_item_initializer(uid) + self.messagelist[uid]['flags'] = flags def readstatus(self, fp): @@ -100,7 +106,10 @@ class LocalStatusFolder(BaseFolder): (line, self.filename) self.ui.warn(errstr) raise ValueError(errstr) - self.messagelist[uid] = {'uid': uid, 'flags': flags, 'mtime': mtime, 'labels': labels} + self.messagelist[uid] = self.msglist_item_initializer(uid) + self.messagelist[uid]['flags'] = flags + self.messagelist[uid]['mtime'] = mtime + self.messagelist[uid]['labels'] = labels # Interface from BaseFolder @@ -197,7 +206,11 @@ class LocalStatusFolder(BaseFolder): self.savemessageflags(uid, flags) return uid - self.messagelist[uid] = {'uid': uid, 'flags': flags, 'time': rtime, 'mtime': mtime, 'labels': labels} + self.messagelist[uid] = self.msglist_item_initializer(uid) + self.messagelist[uid]['flags'] = flags + self.messagelist[uid]['time'] = rtime + self.messagelist[uid]['mtime'] = mtime + self.messagelist[uid]['labels'] = labels self.save() return uid diff --git a/offlineimap/folder/LocalStatusSQLite.py b/offlineimap/folder/LocalStatusSQLite.py index e48545d..a9ef6c2 100644 --- a/offlineimap/folder/LocalStatusSQLite.py +++ b/offlineimap/folder/LocalStatusSQLite.py @@ -184,14 +184,23 @@ class LocalStatusSQLiteFolder(BaseFolder): self._newfolder = True + # Interface from BaseFolder + def msglist_item_initializer(self, uid): + return {'uid': uid, 'flags': set(), 'labels': set(), 'time': 0, 'mtime': 0} + + # Interface from BaseFolder def cachemessagelist(self): self.messagelist = {} cursor = self.connection.execute('SELECT id,flags,mtime,labels from status') for row in cursor: + uid = row[0] + self.messagelist[uid] = self.msglist_item_initializer(uid) flags = set(row[1]) labels = set([lb.strip() for lb in row[3].split(',') if len(lb.strip()) > 0]) - self.messagelist[row[0]] = {'uid': row[0], 'flags': flags, 'mtime': row[2], 'labels': labels} + self.messagelist[uid]['flags'] = flags + self.messagelist[uid]['labels'] = labels + self.messagelist[uid]['mtime'] = row[2] # Interface from LocalStatusFolder def save(self): @@ -271,6 +280,7 @@ class LocalStatusSQLiteFolder(BaseFolder): self.savemessageflags(uid, flags) return uid + self.messagelist[uid] = self.msglist_item_initializer(uid) self.messagelist[uid] = {'uid': uid, 'flags': flags, 'time': rtime, 'mtime': mtime, 'labels': labels} flags = ''.join(sorted(flags)) labels = ', '.join(sorted(labels)) @@ -278,9 +288,11 @@ class LocalStatusSQLiteFolder(BaseFolder): (uid,flags,mtime,labels)) return uid + # Interface from BaseFolder def savemessageflags(self, uid, flags): - self.messagelist[uid] = {'uid': uid, 'flags': flags} + assert self.uidexists(uid) + self.messagelist[uid]['flags'] = flags flags = ''.join(sorted(flags)) self.__sql_write('UPDATE status SET flags=? WHERE id=?',(flags,uid)) diff --git a/offlineimap/folder/Maildir.py b/offlineimap/folder/Maildir.py index d067194..156b39e 100644 --- a/offlineimap/folder/Maildir.py +++ b/offlineimap/folder/Maildir.py @@ -191,7 +191,9 @@ class MaildirFolder(BaseFolder): else: uid = long(uidmatch.group(1)) # 'filename' is 'dirannex/filename', e.g. cur/123,U=1,FMD5=1:2,S - retval[uid] = {'flags': flags, 'filename': filepath} + retval[uid] = self.msglist_item_initializer(uid) + retval[uid]['flags'] = flags + retval[uid]['filename'] = filepath return retval # Interface from BaseFolder @@ -208,6 +210,12 @@ class MaildirFolder(BaseFolder): return True return False #Nope, nothing changed + + # Interface from BaseFolder + def msglist_item_initializer(self, uid): + return {'flags': set(), 'filename': '/no-dir/no-such-file/'} + + # Interface from BaseFolder def cachemessagelist(self): if self.messagelist is None: @@ -319,7 +327,9 @@ class MaildirFolder(BaseFolder): if rtime != None: os.utime(os.path.join(self.getfullname(), tmpname), (rtime, rtime)) - self.messagelist[uid] = {'flags': flags, 'filename': tmpname} + self.messagelist[uid] = self.msglist_item_initializer(uid) + self.messagelist[uid]['flags'] = flags + self.messagelist[uid]['filename'] = tmpname # savemessageflags moves msg to 'cur' or 'new' as appropriate self.savemessageflags(uid, flags) self.ui.debug('maildir', 'savemessage: returning uid %d' % uid) @@ -339,6 +349,9 @@ class MaildirFolder(BaseFolder): Note that this function does not check against dryrun settings, so you need to ensure that it is never called in a dryrun mode.""" + + assert uid in self.messagelist + oldfilename = self.messagelist[uid]['filename'] dir_prefix, filename = os.path.split(oldfilename) # If a message has been seen, it goes into 'cur'