[PATCH 4/4] Another way of locating UID of just saved message

Vladimir.Marek at oracle.com Vladimir.Marek at oracle.com
Tue Jul 26 09:59:56 BST 2011


From: Vladimir Marek <vlmarek at volny.cz>

It works by fetching all headers from new messages from IMAP server an searching
for our X-OfflineIMAP marker by using reular expression.

Signed-off-by: Vladimir Marek <vlmarek at volny.cz>
---
 offlineimap/folder/IMAP.py |   63 ++++++++++++++++++++++++++++++++++++++++++++
 offlineimap/imaplib2.py    |   14 ++++++++++
 2 files changed, 77 insertions(+), 0 deletions(-)

diff --git a/offlineimap/folder/IMAP.py b/offlineimap/folder/IMAP.py
index 3c702f4..1e08760 100644
--- a/offlineimap/folder/IMAP.py
+++ b/offlineimap/folder/IMAP.py
@@ -24,6 +24,7 @@ import time
 from copy import copy
 from Base import BaseFolder
 from offlineimap import imaputil, imaplibutil, OfflineImapError
+from offlineimap.imaplib2 import UnquotedString
 
 class IMAPFolder(BaseFolder):
     def __init__(self, imapserver, name, visiblename, accountname, repository):
@@ -317,6 +318,65 @@ class IMAPFolder(BaseFolder):
         matchinguids.sort()
         return long(matchinguids[0])
 
+    def savemessage_fetchheaders(self, imapobj, headername, headervalue):
+        """ We fetch all new mail headers and search for the right
+        X-OfflineImap line by hand. The response from the server has form:
+        (
+          'OK',
+          [
+            (
+              '185 (RFC822.HEADER {1789}',
+              '... mail headers ...'
+            ),
+            ' UID 2444)',
+            (
+              '186 (RFC822.HEADER {1789}',
+              '... 2nd mail headers ...'
+            ),
+            ' UID 2445)'
+          ]
+        )
+        We need to locate the UID just after mail headers containing our
+        X-OfflineIMAP line.
+
+        Returns UID when found, 0 when not found.
+        """
+        self.ui.debug('imap', 'savemessage_fetchheaders called for %s: %s' % \
+                 (headername, headervalue))
+
+        # run "fetch X:* rfc822.header"
+        # since we stored the mail we are looking for just recently, it would
+        # not be optimal to fetch all messages. So we'll find highest message
+        # UID in our local messagelist and search from there (exactly from
+        # UID+1). That works because UIDs are guaranteed to be unique and
+        # ascending.
+
+        # The problem is that imapobj quotes all parameters. That must not happen
+        # with range X:*. So we use UnquotedString for that
+
+        start = 1+max(self.getmessagelist().keys())
+        result = imapobj.uid('FETCH', UnquotedString('%d:*' % start), 'rfc822.header')
+        assert result[0] == 'OK', 'Error with fetching mail headers: ' + '. '.join(result[1])
+
+        result = result[1]
+
+        found = 0
+        for item in result:
+            if found == 0 and type(item) == type( () ):
+                # Walk just tuples
+                if re.search("(?:^|\\r|\\n)%s:\s*%s(?:\\r|\\n)" % (headername, headervalue), item[1], flags=re.IGNORECASE):
+                    found = 1
+            elif found == 1:
+                if type(item) == type (""):
+                    uid = re.search("UID\s+(\d+)", item, flags=re.IGNORECASE)
+                    if uid:
+                        return int(uid.group(1))
+                    else:
+                        self.ui.warn("Can't parse FETCH response, can't find UID: %s", result.__repr__())
+                else:
+                    self.ui.warn("Can't parse FETCH response, we awaited string: %s", result.__repr__())
+
+        return 0
 
     def getmessageinternaldate(self, content, rtime=None):
         """Parses mail and returns an INTERNALDATE string
@@ -485,6 +545,9 @@ class IMAPFolder(BaseFolder):
                     assert(imapobj.noop()[0] == 'OK')
                     uid = self.savemessage_searchforheader(imapobj, headername,
                                                        headervalue)
+                    if uid == 0:
+                        self.ui.debug('imap', 'savemessage: second attempt to get new UID failed. Going to try search headers manually')
+                        uid = self.savemessage_fetchheaders(imapobj, headername, headervalue)
 
         finally:
             self.imapserver.releaseconnection(imapobj)
diff --git a/offlineimap/imaplib2.py b/offlineimap/imaplib2.py
index ec6cd0d..0d6dcf5 100644
--- a/offlineimap/imaplib2.py
+++ b/offlineimap/imaplib2.py
@@ -185,6 +185,20 @@ class Request(object):
 
 
 
+class UnquotedString(object):
+    """ String which will not be quoted when passed to IMAP4 command. It works
+    because IMAP._checkquote skips anything which is not of a 'basestring'
+    type.
+    Fro example: 
+        imapobj.uid('FETCH', UnquotedString('*:*'), 'rfc822.header')"""
+    t=""
+
+    def __init__(self, text):
+            self.t=text
+
+    def __repr__(self):
+            return self.t
+
 
 class IMAP4(object):
 
-- 
1.7.3.2





More information about the OfflineIMAP-project mailing list