[PATCH v4] Re: make maxage use UIDs to avoid timezone issues

Janna Martl janna.martl109 at gmail.com
Mon Apr 6 08:31:51 BST 2015


On Fri, Mar 27, 2015 at 02:38:02AM -0400, Janna Martl wrote:
>Thanks to UIDMaps magic, it turns out that this works without
>modification for the case when the local folder has type IMAP. The mess
>I noticed before is because I was using type Gmail, and Gmail-IMAP
>sync isn't supported (yet). In the IMAP case, UIDMaps.py defines a
>MappedIMAPFolder class, so I made a MappedGmailFolder case that inherits
>from GmailFolder and MappedIMAPFolder. I tried to check this in some
>basic cases (copying to/from empty folder, copying/ deleting messages,
>with/without maxsize) but this all seems too good to be true and I can't
>shake the feeling that I missed something.
>
>
>In order to do IMAP-IMAP sync, the local folder needs to inherit from
>MappedIMAPFolder. Make this work for Gmail as the local folder.


My previous attempt at this was very wrong. Try #2.

The one thing that's still not done with this is the unceremoniously
commented out bit about mtimes.

def syncmessagesto_labels(self, dstfolder, statusfolder):
<...>	
   for i, uid in enumerate(uidlist):
   <...>
#       mtime = dstfolder.getmessagemtime(uid)
#       mtimes[uid] = mtime
       labels[uid] = selflabels

#            statusfolder.savemessagesmtimebulk(mtimes)

If dstfolder is another IMAP folder, then getmessagemtime isn't
implemented, so this throws an error. What should this be, if anything?


Add MappedGmailFolder class extending MappedIMAPFolder and Gmail to
allow Gmail to be used as the local folder in an IMAP-IMAP sync.
---
 offlineimap/folder/Gmail.py        | 45 +++++++++++++++++++++++++++++++++-----
 offlineimap/folder/UIDMaps.py      |  3 ++-
 offlineimap/repository/Gmail.py    |  5 +++++
 offlineimap/repository/__init__.py |  3 ++-
 4 files changed, 49 insertions(+), 7 deletions(-)

diff --git a/offlineimap/folder/Gmail.py b/offlineimap/folder/Gmail.py
index 354d544..56c5bd4 100644
--- a/offlineimap/folder/Gmail.py
+++ b/offlineimap/folder/Gmail.py
@@ -23,6 +23,7 @@ from offlineimap import imaputil, OfflineImapError
 from offlineimap import imaplibutil
 import offlineimap.accounts
 from .IMAP import IMAPFolder
+from offlineimap.folder.UIDMaps import MappedIMAPFolder
 
 """Folder implementation to support features of the Gmail IMAP server."""
 
@@ -229,7 +230,7 @@ class GmailFolder(IMAPFolder):
                 return retlabels
         return None
 
-    def savemessagelabels(self, uid, labels):
+    def savemessagelabels(self, uid, labels, ignorelabels=set()):
         """Change a message's labels to `labels`.
 
         Note that this function does not check against dryrun settings,
@@ -358,15 +359,49 @@ class GmailFolder(IMAPFolder):
                     self.ui.settinglabels(uid, i+1, len(uidlist), sorted(selflabels), dstfolder)
                     if self.repository.account.dryrun:
                         continue #don't actually add in a dryrun
-                    dstfolder.savemessagelabels(uid, selflabels, ignorelabels = self.ignorelabels)
-                    mtime = dstfolder.getmessagemtime(uid)
-                    mtimes[uid] = mtime
+                    dstfolder.savemessagelabels(uid, selflabels, ignorelabels=self.ignorelabels)
+#                    mtime = dstfolder.getmessagemtime(uid)
+#                    mtimes[uid] = mtime
                     labels[uid] = selflabels
 
             # Update statusfolder in a single DB transaction. It is safe, as if something fails,
             # statusfolder will be updated on the next run.
             statusfolder.savemessageslabelsbulk(labels)
-            statusfolder.savemessagesmtimebulk(mtimes)
+#            statusfolder.savemessagesmtimebulk(mtimes)
 
         except NotImplementedError:
             self.ui.warn("Can't sync labels. You need to configure a local repository of type GmailMaildir")
+
+class MappedGmailFolder(MappedIMAPFolder, GmailFolder):
+    def __init__(self, *args, **kwargs):
+        MappedIMAPFolder.__init__(self, *args, **kwargs)
+        GmailFolder.__init__(self, *args, **kwargs)
+
+    def savemessagelabels(self, uid, labels, ignorelabels=set()):
+        self.messagelist = self._mb.messagelist
+        super(MappedGmailFolder, self).savemessagelabels(self.r2l[uid],
+            labels, ignorelabels)
+
+    def getmessage(self, uid):
+        self.messagelist = self._mb.messagelist
+        return super(MappedGmailFolder, self).getmessage(uid)
+
+    def getmessagelabels(self, uid):
+        self.messagelist = self._mb.messagelist
+        return super(MappedGmailFolder, self).getmessagelabels(self.r2l[uid])
+
+    def cachemessagelist(self, min_date=None, min_uid=None):
+        # min_uid is stored as the real local value, not the remote counterpart,
+        # so this can be passed as is
+        super(MappedGmailFolder, self).cachemessagelist(min_date=min_date, 
+            min_uid=min_uid)
+        self.messagelist = self._mb.messagelist
+
+    def deletemessageslabels(self, uidlist, labels):
+        uidlistl = map(lambda uid : self.r2l[uid])
+        super(MappedGmailFolder, self).deletemessageslabels(uidlistl, labels)
+
+    def addmessageslabels(self, uidlist, labels):
+        uidlistl = map(lambda uid : self.r2l[uid])
+        super(MappedGmailFolder, self).addmessageslabels(uidlistl, labels)
+
diff --git a/offlineimap/folder/UIDMaps.py b/offlineimap/folder/UIDMaps.py
index 95acbe5..167bfc3 100644
--- a/offlineimap/folder/UIDMaps.py
+++ b/offlineimap/folder/UIDMaps.py
@@ -95,7 +95,8 @@ class MappedIMAPFolder(IMAPFolder):
 
     # Interface from BaseFolder
     def cachemessagelist(self, min_date=None, min_uid=None):
-        self._mb.cachemessagelist(min_date=min_date)
+        self._mb.cachemessagelist(min_date=min_date, min_uid=min_uid)
+        self.messagelist = self._mb.messagelist
         reallist = self._mb.getmessagelist()
 
         self.maplock.acquire()
diff --git a/offlineimap/repository/Gmail.py b/offlineimap/repository/Gmail.py
index 2e23e62..f85f306 100644
--- a/offlineimap/repository/Gmail.py
+++ b/offlineimap/repository/Gmail.py
@@ -16,6 +16,7 @@
 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
 from offlineimap.repository.IMAP import IMAPRepository
+from offlineimap.folder.Gmail import MappedGmailFolder
 from offlineimap import folder, OfflineImapError
 
 class GmailRepository(IMAPRepository):
@@ -72,3 +73,7 @@ class GmailRepository(IMAPRepository):
     def getspamfolder(self):
         #: Gmail also deletes messages upon EXPUNGE in the Spam folder
         return  self.getconf('spamfolder','[Gmail]/Spam')
+
+class MappedGmailRepository(GmailRepository):
+    def getfoldertype(self):
+        return MappedGmailFolder
diff --git a/offlineimap/repository/__init__.py b/offlineimap/repository/__init__.py
index 0fbbc13..4b9ded4 100644
--- a/offlineimap/repository/__init__.py
+++ b/offlineimap/repository/__init__.py
@@ -23,7 +23,7 @@ except ImportError: #python2
     from ConfigParser import NoSectionError
 
 from offlineimap.repository.IMAP import IMAPRepository, MappedIMAPRepository
-from offlineimap.repository.Gmail import GmailRepository
+from offlineimap.repository.Gmail import GmailRepository, MappedGmailRepository
 from offlineimap.repository.Maildir import MaildirRepository
 from offlineimap.repository.GmailMaildir import GmailMaildirRepository
 from offlineimap.repository.LocalStatus import LocalStatusRepository
@@ -49,6 +49,7 @@ class Repository(object):
         elif reqtype == 'local':
             name = account.getconf('localrepository')
             typemap = {'IMAP': MappedIMAPRepository,
+                'Gmail': MappedGmailRepository,
                 'Maildir': MaildirRepository,
                 'GmailMaildir': GmailMaildirRepository}
 
-- 
2.3.5




More information about the OfflineIMAP-project mailing list