fix race when receiving mail during rule process
[imapami.git] / imapami / __init__.py
index 5000e77..b817646 100644 (file)
@@ -37,6 +37,7 @@ import imaplib
 import inspect
 import logging
 import pydoc
+import re
 import sys
 import yaml
 
@@ -111,6 +112,7 @@ class Imapami(object):
         self.logger = self._get_logger(loglevel)
         self._load_config(config)
         self._update_logger()
+        self.uidnext = {}
 
     def _get_logger(self, loglevel):
         """
@@ -211,14 +213,38 @@ class Imapami(object):
         imap.login(login, password)
         self.imap = imap
 
+    def get_uidnext(self):
+        """
+        Get the state (uidnext) for each inbox used in the configuration.
+        It gives the uid of the next message the will be added in the mbox.
+        We will only care about messages with a uid lower than this uidnext,
+        to avoid race conditions with a message arriving while we are in the
+        middle of rules processing.
+        """
+        self.logger.info('Getting inbox state...')
+        mboxes = [self.config["inbox"]] + [rule.inbox for rule in self.rules]
+        for m in mboxes:
+            if m is None:
+                continue
+            if self.uidnext.get(m, None) is not None:
+                continue
+            self.imap.select(m)
+            typ, dat = self.imap.status(m, "(UIDNEXT)")
+            if typ != 'OK':
+                raise ValueError("cannot get UIDNEXT: %s", typ)
+            match = re.match("[^ ]* \(UIDNEXT ([0-9]+)\)", dat[0])
+            if match is None:
+                raise ValueError("cannot match UIDNEXT: %s", typ)
+            self.uidnext[m] = int(match.groups()[0])
+        self.logger.info('Done: %r', self.uidnext)
+
     def process_rules(self):
         """
         Process the rules.
         """
         self.logger.info('Processing rules...')
-        inbox = self.config["inbox"]
         for rule in self.rules:
-            rule.process(self, inbox)
+            rule.process(self)
         self.logger.info('Done.')
 
     def close(self):
@@ -318,6 +344,7 @@ def main():
         sys.exit(0)
 
     p.connect()
+    p.get_uidnext()
     p.process_rules()
     p.close()