# HG changeset patch # User Goffi # Date 1295234611 -3600 # Node ID 9fc32d1d90463012d722df64f52d1e0724aa6e50 # Parent f45ffbf211e945baffe9fa03a8c363f992fe91ab Plugin IMAP, plugin MAILDIR: added IMAP's UID management, mailbox data persistence diff -r f45ffbf211e9 -r 9fc32d1d9046 src/plugins/plugin_misc_imap.py --- 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. diff -r f45ffbf211e9 -r 9fc32d1d9046 src/plugins/plugin_misc_maildir.py --- 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