changeset 254:9fc32d1d9046

Plugin IMAP, plugin MAILDIR: added IMAP's UID management, mailbox data persistence
author Goffi <goffi@goffi.org>
date Mon, 17 Jan 2011 04:23:31 +0100
parents f45ffbf211e9
children 55b750017b71
files src/plugins/plugin_misc_imap.py src/plugins/plugin_misc_maildir.py
diffstat 2 files changed, 171 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/src/plugins/plugin_misc_imap.py	Mon Jan 17 00:15:50 2011 +0100
+++ b/src/plugins/plugin_misc_imap.py	Mon Jan 17 04:23:31 2011 +0100
@@ -85,6 +85,7 @@
         """Retrieve the unique identifier associated with this message.
         """
         debug('getUID (message)')
+        debug ('===>%i', self.uid)
         return self.uid
 
     def getFlags(self):
@@ -176,7 +177,7 @@
         """Return the likely UID for the next message added to this mailbox.
         """
         debug ('getUIDNext')
-        return self.getMessageCount()+1
+        return self.mailbox.getNextUid()
 
     def getUID(self,message):
         """Return the UID of a message in the mailbox
@@ -184,7 +185,7 @@
         @return: The UID of the message.
         """
         debug ('getUID')
-        return self.mailbox.getId(message)
+        return self.mailbox.getUid(message)
 
     def getMessageCount(self):
         """Return the number of messages in this mailbox.
@@ -285,11 +286,24 @@
         @param uid: If true, the IDs specified in the query are UIDs;
         """
         debug('fetch (%s, %s)'%(messages,uid))
-        messages.last = self.getMessageCount()
-        for mes_idx in messages:
-            if mes_idx>self.getMessageCount():
-                continue
-            yield (mes_idx,Message(mes_idx,self.mailbox.getMessage(mes_idx-1)))
+        if uid:
+            messages.last = self.mailbox.getMaxUid()
+            messages.getnext = self.mailbox.getNextExistingUid
+            for mess_uid in messages:
+                if mess_uid == None:
+                    debug ('stopping iteration')
+                    raise StopIteration
+                try:
+                    debug ('yielding (%s,%s)' % (mess_uid,Message(mess_uid,self.mailbox.getMessageUid(mess_uid))))
+                    yield (mess_uid,Message(mess_uid,self.mailbox.getMessageUid(mess_uid)))
+                except IndexError:
+                    continue
+        else:
+            messages.last = self.getMessageCount()
+            for mess_idx in messages:
+                if mess_idx>self.getMessageCount():
+                    raise StopIteration
+                yield (mess_idx,Message(mess_idx,self.mailbox.getMessage(mess_idx-1)))
 
     def store(self, messages, flags, mode, uid):
         """Set the flags of one or more messages.
--- a/src/plugins/plugin_misc_maildir.py	Mon Jan 17 00:15:50 2011 +0100
+++ b/src/plugins/plugin_misc_maildir.py	Mon Jan 17 04:23:31 2011 +0100
@@ -62,10 +62,18 @@
 
         self.__observed={}
         self.__mailboxes={}
+        self.data=host.memory.getPrivate("MAILDIR_data") or {"INBOX":{"cur_idx":0}}
+        #a value in the dictionnary for a mailbox is a dictionnary with the following value
+        #    - cur_idx: value of the current unique integer increment (UID)
+        #    - message_id (as returned by MaildirMailbox): a tuple of (UID, [flag1, flag2, ...])
 
         #the trigger
         host.trigger.add("MessageReceived", self.MessageReceivedTrigger)
 
+    def __destroy__(self):
+        debug('Destroying MaildirBox')
+        self.host.memory.setPrivate('MAILDIR_data',self.data)
+
     def accessMessageBox(self, boxname, observer=None):
         """Create and return a MailboxUser instance
         @param boxname: name of the box
@@ -95,8 +103,109 @@
         if self.__mailboxes.has_key(boxname):
             return self.__mailboxes[boxname]
 
+    def getUid(self, boxname, message_id):
+        """Return an unique integer, always ascending, for a message
+        This is mainly needed for the IMAP protocol
+        @param boxname: name of the box where the message is
+        @param message_id: unique id of the message as given by MaildirMailbox
+        @return: Integer UID"""
+        try:
+            box_data = self.data[boxname] #the boxname MUST exist in the data
+        except KeyError:
+            err_msg=_("Boxname doesn't exist in internal data")
+            error(_("INTERNAL ERROR: ") + err_msg)
+            raise MaildirError(err_msg)
+        if box_data.has_key(message_id):
+            ret = box_data[message_id][0]
+        else:
+            box_data['cur_idx']+=1
+            box_data[message_id]=(box_data['cur_idx'],[])
+            ret = box_data[message_id]
+        self.host.memory.setPrivate('MAILDIR_data',self.data)
+        return ret
 
 
+    def getNextUid(self, boxname):
+        """Return next unique integer that will generated
+        This is mainly needed for the IMAP protocol
+        @param boxname: name of the box where the message is
+        @return: Integer UID"""
+        try:
+            box_data = self.data[boxname] #the boxname MUST exist in the data
+        except KeyError:
+            err_msg=_("Boxname doesn't exist in internal data")
+            error(_("INTERNAL ERROR: ") + err_msg)
+            raise MaildirError(err_msg)
+        return box_data['cur_idx']+1
+
+    def getNextExistingUid(self, boxname, uid):
+        """Give the next uid of existing message
+        @param boxname: name of the box where the message is
+        @param uid: uid to start from
+        @return: uid or None if the is no more message"""
+        try:
+            box_data = self.data[boxname] #the boxname MUST exist in the data
+        except KeyError:
+            err_msg=_("Boxname doesn't exist in internal data")
+            error(_("INTERNAL ERROR: ") + err_msg)
+            raise MaildirError(err_msg)
+        idx=uid+1
+        while self.getIdfromUid(boxname, idx) == None: #TODO: this is highly inefficient because getIdfromUid is inefficient, fix this
+            idx+=1
+            if idx>box_data['cur_idx']:
+                return None
+        return idx
+    
+    def getMaxUid(self, boxname):
+        """Give the max existing uid
+        @param boxname: name of the box where the message is
+        @return: uid"""
+        try:
+            box_data = self.data[boxname] #the boxname MUST exist in the data
+        except KeyError:
+            err_msg=_("Boxname doesn't exist in internal data")
+            error(_("INTERNAL ERROR: ") + err_msg)
+            raise MaildirError(err_msg)
+        return box_data['cur_idx']
+    
+    
+    def getIdfromUid(self, boxname, message_uid):
+        """Return the message unique id from it's integer UID
+        @param boxname: name of the box where the message is
+        @param message_uid: unique integer identifier
+        @return: unique id of the message as given by MaildirMailbox or None if not found"""
+        try:
+            box_data = self.data[boxname] #the boxname MUST exist in the data
+        except KeyError:
+            err_msg=_("Boxname doesn't exist in internal data")
+            error(_("INTERNAL ERROR: ") + err_msg)
+            raise MaildirError(err_msg)
+
+        for message_id in box_data.keys(): #TODO: this is highly inefficient on big mailbox, must be replaced in the future
+            if message_id == 'cur_idx':
+                continue
+            if box_data[message_id][0] == message_uid:
+                return message_id
+        return None
+
+    def cleanTable(self, boxname, existant_id):
+        """Remove mails which no longuer exist from the table
+        @param boxname: name of the box to clean
+        @param existant_id: list of id which actually exist"""
+        try:
+            box_data = self.data[boxname] #the boxname MUST exist in the data
+        except KeyError:
+            err_msg=_("Boxname doesn't exist in internal data")
+            error(_("INTERNAL ERROR: ") + err_msg)
+            raise MaildirError(err_msg)
+        to_remove=[]
+        for key in box_data:
+            if key not in existant_id and key!="cur_idx":
+                to_remove.append(key)
+        for key in to_remove:
+            del box_data[key]
+        
+
     def MessageReceivedTrigger(self, message):
         """This trigger catch normal message and put the in the Maildir box.
         If the message is not of "normal" type, do nothing
@@ -187,10 +296,22 @@
         self.mailbox_path=mailbox_path
         self.mailbox = maildir.MaildirMailbox(mailbox_path)
         self.observer=observer
+        self.__uid_table_update()
+        
+
         if observer:
             debug("adding observer for %s" % name)
             self.maildir.addObserver(observer, name, "NEW_MESSAGE")
 
+    def __uid_table_update(self):
+        existant_id=[]
+        for mess_idx in range (self.getMessageCount()):
+            #we update the uid table
+            existant_id.append(self.getId(mess_idx))
+            self.getUid(mess_idx)
+        self.maildir.cleanTable(self.name, existant_id)
+    
+
     def __destroy__(self):
         if observer:
             debug("removing observer for %s" % self.name)
@@ -204,7 +325,7 @@
 
     def emitSignal(self, ignore, signal):
         """Emit the signal to the observers"""
-        print ('self: %s, mailbox: %s, count: %i' % (self, self.mailbox, self.getMessageCount()))
+        self.getUid(self.getMessageCount()-1) #we make an uid for the last message added
         self.maildir.emitSignal(self.name, signal)
 
     def getId(self, mess_idx):
@@ -212,12 +333,38 @@
         @mess_idx: message index"""
         return self.mailbox.getUidl(mess_idx)
 
+    def getUid(self, mess_idx):
+        """Return a unique interger id for the message, always ascending"""
+        mess_id=self.getId(mess_idx)
+        return self.maildir.getUid(self.name,mess_id)
+
+    def getNextUid(self):
+        return self.maildir.getNextUid(self.name)
+
+    def getNextExistingUid(self, uid):
+        return self.maildir.getNextExistingUid(self.name, uid)
+
+    def getMaxUid(self):
+        return self.maildir.getMaxUid(self.name)
+
     def getMessageCount(self):
         """Return number of mails present in this box"""
-        print "count: %i" % len(self.mailbox.listMessages())
-        return len(self.mailbox.listMessages())
+        return len(self.mailbox.list)
 
+    def getMessageIdx(self, mess_idx):
+        """Return the full message
+        @mess_idx: message index"""
+        return self.mailbox.getMessage(mess_idx)
+    
     def getMessage(self, mess_idx):
         """Return the full message
         @mess_idx: message index"""
         return self.mailbox.getMessage(mess_idx)
+
+    def getMessageUid(self, mess_uid):
+        """Return the full message
+        @mess_idx: message unique identifier"""
+        for mess_idx in range (self.getMessageCount()):
+            if self.getUid(mess_idx) == mess_uid:
+                return self.mailbox.getMessage(mess_idx)
+        raise IndexError