]> code.delx.au - offlineimap/blobdiff - offlineimap/folder/IMAP.py
Revert "completed: * fixes behaviour when changing flags, without corresp. rights...
[offlineimap] / offlineimap / folder / IMAP.py
index d89059670e2985668192ae793365f467b2369cf4..efba10c47fe8852ed7601cd3b411f19975007810 100644 (file)
@@ -1,5 +1,5 @@
 # IMAP folder support
-# Copyright (C) 2002-2004 John Goerzen
+# Copyright (C) 2002-2007 John Goerzen
 # <jgoerzen@complete.org>
 #
 #    This program is free software; you can redistribute it and/or modify
@@ -17,7 +17,8 @@
 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
 from Base import BaseFolder
-from offlineimap import imaputil, imaplib
+import imaplib
+from offlineimap import imaputil, imaplibutil
 from offlineimap.ui import UIBase
 from offlineimap.version import versionstr
 import rfc822, time, string, random, binascii, re
@@ -40,6 +41,16 @@ class IMAPFolder(BaseFolder):
         self.randomgenerator = random.Random()
         BaseFolder.__init__(self)
 
+    def selectro(self, imapobj):
+        """Select this folder when we do not need write access.
+        Prefer SELECT to EXAMINE if we can, since some servers
+        (Courier) do not stabilize UID validity until the folder is
+        selected."""
+        try:
+            imapobj.select(self.getfullname())
+        except imapobj.readonly:
+            imapobj.select(self.getfullname(), readonly = 1)
+
     def getaccountname(self):
         return self.accountname
 
@@ -59,11 +70,52 @@ class IMAPFolder(BaseFolder):
         imapobj = self.imapserver.acquireconnection()
         try:
             # Primes untagged_responses
-            imapobj.select(self.getfullname(), readonly = 1)
+            self.selectro(imapobj)
             return long(imapobj.untagged_responses['UIDVALIDITY'][0])
         finally:
             self.imapserver.releaseconnection(imapobj)
     
+    def quickchanged(self, statusfolder):
+        # An IMAP folder has definitely changed if the number of
+        # messages or the UID of the last message have changed.  Otherwise
+        # only flag changes could have occurred.
+        imapobj = self.imapserver.acquireconnection()
+        try:
+            # Primes untagged_responses
+            imapobj.select(self.getfullname(), readonly = 1, force = 1)
+            try:
+                # Some mail servers do not return an EXISTS response if
+                # the folder is empty.
+                maxmsgid = long(imapobj.untagged_responses['EXISTS'][0])
+            except KeyError:
+                return True
+
+            # Different number of messages than last time?
+            if maxmsgid != len(statusfolder.getmessagelist()):
+                return True
+
+            if maxmsgid < 1:
+                # No messages; return
+                return False
+
+            # Now, get the UID for the last message.
+            response = imapobj.fetch('%d' % maxmsgid, '(UID)')[1]
+        finally:
+            self.imapserver.releaseconnection(imapobj)
+
+        # Discard the message number.
+        messagestr = string.split(response[0], maxsplit = 1)[1]
+        options = imaputil.flags2hash(messagestr)
+        if not options.has_key('UID'):
+            return True
+        uid = long(options['UID'])
+        saveduids = statusfolder.getmessagelist().keys()
+        saveduids.sort()
+        if uid != saveduids[-1]:
+            return True
+
+        return False
+
     def cachemessagelist(self):
         imapobj = self.imapserver.acquireconnection()
         self.messagelist = {}
@@ -98,7 +150,7 @@ class IMAPFolder(BaseFolder):
             else:
                 uid = long(options['UID'])
                 flags = imaputil.flagsimap2maildir(options['FLAGS'])
-                rtime = imaplib.Internaldate2epoch(messagestr)
+                rtime = imaplibutil.Internaldate2epoch(messagestr)
                 self.messagelist[uid] = {'uid': uid, 'flags': flags, 'time': rtime}
 
     def getmessagelist(self):
@@ -212,6 +264,12 @@ class IMAPFolder(BaseFolder):
             try:
                 if datetuple[0] < 1981:
                     raise ValueError
+
+                # Check for invalid date
+                datetuple_check = time.localtime(time.mktime(datetuple))
+                if datetuple[:2] != datetuple_check[:2]:
+                    raise ValueError
+
                 # This could raise a value error if it's not a valid format.
                 date = imaplib.Time2Internaldate(datetuple) 
             except (ValueError, OverflowError):
@@ -270,7 +328,7 @@ class IMAPFolder(BaseFolder):
                 return
             result = imapobj.uid('store', '%d' % uid, 'FLAGS',
                                  imaputil.flagsmaildir2imap(flags))
-            assert result[0] == 'OK', 'Error with store: ' + r[1]
+            assert result[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
         finally:
             self.imapserver.releaseconnection(imapobj)
         result = result[1][0]
@@ -310,13 +368,14 @@ class IMAPFolder(BaseFolder):
             try:
                 imapobj.select(self.getfullname())
             except imapobj.readonly:
+               # unsure, whether this can be reached
                 UIBase.getglobalui().flagstoreadonly(self, uidlist, flags)
                 return
             r = imapobj.uid('store',
                             imaputil.listjoin(uidlist),
                             operation + 'FLAGS',
                             imaputil.flagsmaildir2imap(flags))
-            assert r[0] == 'OK', 'Error with store: ' + r[1]
+            assert r[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
             r = r[1]
         finally:
             self.imapserver.releaseconnection(imapobj)
@@ -333,9 +392,9 @@ class IMAPFolder(BaseFolder):
             if not ('UID' in attributehash and 'FLAGS' in attributehash):
                 # Compensate for servers that don't return a UID attribute.
                 continue
-            flags = attributehash['FLAGS']
+            lflags = attributehash['FLAGS']
             uid = long(attributehash['UID'])
-            self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
+            self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(lflags)
             try:
                 needupdate.remove(uid)
             except ValueError:          # Let it slide if it's not in the list