# HG changeset patch # User Goffi # Date 1295295976 -3600 # Node ID 55b750017b712d905ffae8d6be96697a3b01dfb8 # Parent 9fc32d1d90463012d722df64f52d1e0724aa6e50 plugin IMAP, plugin Maildir: added flag, IMAP's uid management diff -r 9fc32d1d9046 -r 55b750017b71 src/plugins/plugin_misc_imap.py --- a/src/plugins/plugin_misc_imap.py Mon Jan 17 04:23:31 2011 +0100 +++ b/src/plugins/plugin_misc_imap.py Mon Jan 17 21:26:16 2011 +0100 @@ -75,9 +75,10 @@ class Message(): implements(imap4.IMessage) - def __init__(self, uid, mess_fp): + def __init__(self, uid, flags, mess_fp): debug('Message Init') self.uid=uid + self.flags=flags self.mess_fp=mess_fp self.message=Parser().parse(mess_fp) @@ -93,7 +94,7 @@ @return: The flags, represented as strings. """ debug('getFlags') - return [] + return self.flags def getInternalDate(self): """Retrieve the date internally associated with this message. @@ -184,8 +185,9 @@ @param message: The message sequence number @return: The UID of the message. """ - debug ('getUID') - return self.mailbox.getUid(message) + debug ('getUID (%i)' % message) + #return self.mailbox.getUid(message-1) #XXX: it seems that this method get uid and not message sequence number + return message def getMessageCount(self): """Return the number of messages in this mailbox. @@ -199,13 +201,13 @@ """Return the number of messages with the 'Recent' flag. """ debug('getRecentCount') - return 0 + return len(self.mailbox.getMessageIdsWithFlag('\\Recent')) def getUnseenCount(self): """Return the number of messages with the 'Unseen' flag. """ debug('getUnseenCount') - return 1 + return self.getMessageCount()-len(self.mailbox.getMessageIdsWithFlag('\\Seen')) def isWriteable(self): """Get the read/write status of the mailbox. @@ -277,7 +279,7 @@ or a Deferred whose callback will be invoked with such a list. """ debug('expunge') - raise NotImplementedError + self.mailbox.removeDeleted() def fetch(self, messages, uid): """Retrieve one or more messages. @@ -294,8 +296,7 @@ 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))) + yield (mess_uid,Message(mess_uid,self.mailbox.getFlagsUid(mess_uid), self.mailbox.getMessageUid(mess_uid))) except IndexError: continue else: @@ -303,7 +304,7 @@ for mess_idx in messages: if mess_idx>self.getMessageCount(): raise StopIteration - yield (mess_idx,Message(mess_idx,self.mailbox.getMessage(mess_idx-1))) + yield (mess_idx,Message(mess_idx,self.mailbox.getFlags(mess_idx),self.mailbox.getMessage(mess_idx-1))) def store(self, messages, flags, mode, uid): """Set the flags of one or more messages. @@ -321,7 +322,29 @@ such a dict. """ debug('store') - raise NotImplementedError + + def updateFlags(getF,setF): + ret = {} + for mess_id in messages: + if (uid and mess_id == None) or (not uid and mess_id>self.getMessageCount()): + break + _flags=set(getF(mess_id) if mode else []) + if mode==-1: + _flags.difference_update(set(flags)) + else: + _flags.update(set(flags)) + new_flags=list(_flags) + setF(mess_id, new_flags) + ret[mess_id] = new_flags + return ret + + if uid: + messages.last = self.mailbox.getMaxUid() + messages.getnext = self.mailbox.getNextExistingUid + return updateFlags(self.mailbox.getFlagsUid,self.mailbox.setFlagsUid) + else: + messages.last = self.getMessageCount() + return updateFlags(self.mailbox.getFlags,self.mailbox.setFlags) def getFlags(self): """Return the flags defined in this mailbox @@ -329,8 +352,7 @@ @return: A list of the flags that can be set on messages in this mailbox. """ debug('getFlags') - #return ['\Seen','\Answered','\Flagged','\Deleted','\Draft', '\Recent'] - return [] + return ['\Seen','\Answered','\Flagged','\Deleted','\Draft'] #TODO: add '\Recent' def getHierarchicalDelimiter(self): """Get the character which delimits namespaces for in this mailbox. diff -r 9fc32d1d9046 -r 55b750017b71 src/plugins/plugin_misc_maildir.py --- a/src/plugins/plugin_misc_maildir.py Mon Jan 17 04:23:31 2011 +0100 +++ b/src/plugins/plugin_misc_maildir.py Mon Jan 17 21:26:16 2011 +0100 @@ -70,7 +70,7 @@ #the trigger host.trigger.add("MessageReceived", self.MessageReceivedTrigger) - def __destroy__(self): + def __del__(self): debug('Destroying MaildirBox') self.host.memory.setPrivate('MAILDIR_data',self.data) @@ -103,39 +103,37 @@ if self.__mailboxes.has_key(boxname): return self.__mailboxes[boxname] + def __getBoxData(self, boxname): + """Return the date of a box""" + try: + return 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) + 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) + box_data = self.__getBoxData(boxname) 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'],[]) + 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) + box_data = self.__getBoxData(boxname) return box_data['cur_idx']+1 def getNextExistingUid(self, boxname, uid): @@ -143,14 +141,9 @@ @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) + box_data = self.__getBoxData(boxname) idx=uid+1 - while self.getIdfromUid(boxname, idx) == None: #TODO: this is highly inefficient because getIdfromUid is inefficient, fix this + 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 @@ -160,27 +153,15 @@ """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) + box_data = self.__getBoxData(boxname) return box_data['cur_idx'] - - def getIdfromUid(self, boxname, message_uid): + 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) - + box_data = self.__getBoxData(boxname) 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 @@ -188,16 +169,58 @@ return message_id return None + def getFlags(self, boxname, mess_id): + """Return the messages flags + @param boxname: name of the box where the message is + @param message_idx: message id as given by MaildirMailbox + @return: list of strings""" + box_data = self.__getBoxData(boxname) + if not box_data.has_key(mess_id): + raise MailboxException("Trying to get flags from an unexisting message") + return box_data[mess_id][1] + + def setFlags(self, boxname, mess_id, flags): + """Change the flags of the message + @param boxname: name of the box where the message is + @param message_idx: message id as given by MaildirMailbox + @param flags: list of strings + """ + box_data = self.__getBoxData(boxname) + assert(type(flags)==list) + if not box_data.has_key(mess_id): + raise MailboxException("Trying to set flags for an unexisting message") + box_data[mess_id][1]=flags + self.host.memory.setPrivate('MAILDIR_data',self.data) + + def getMessageIdsWithFlag(self, boxname, flag): + """Return ids of messages where a flag is set + @param boxname: name of the box where the message is + @param flag: flag to check + @return: list of id (as given by MaildirMailbox)""" + box_data = self.__getBoxData(boxname) + assert(isinstance(flag,basestring)) + result = [] + for key in box_data: + if key=='cur_idx': + continue + if flag in box_data[key][1]: + result.append(key) + return result + + def purgeDeleted(self, boxname): + """Remove data for messages with flag "\\Deleted" + @param boxname: name of the box where the message is + """ + box_data = self.__getBoxData(boxname) + for mess_id in self.getMessageIdsWithFlag(boxname,"\\Deleted"): + del(box_data[mess_id]) + self.host.memory.setPrivate('MAILDIR_data',self.data) + 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) + box_data = self.__getBoxData(boxname) to_remove=[] for key in box_data: if key not in existant_id and key!="cur_idx": @@ -312,7 +335,7 @@ self.maildir.cleanTable(self.name, existant_id) - def __destroy__(self): + def __del__(self): if observer: debug("removing observer for %s" % self.name) self._maildir.removeObserver(observer, self.name, "NEW_MESSAGE") @@ -355,16 +378,83 @@ """Return the full message @mess_idx: message index""" return self.mailbox.getMessage(mess_idx) + + def getIdxFromUid(self, mess_uid): + """Return the message index from the uid + @param mess_uid: message unique identifier + @return: message index, as managed by MaildirMailbox""" + for mess_idx in range (self.getMessageCount()): + if self.getUid(mess_idx) == mess_uid: + return mess_idx + raise IndexError + + def getIdxFromId(self, mess_id): + """Return the message index from the unique index + @param mess_id: message unique index as given by MaildirMailbox + @return: message sequence index""" + for mess_idx in range (self.getMessageCount()): + if self.mailbox.getUidl(mess_idx) == mess_id: + return mess_idx + raise IndexError def getMessage(self, mess_idx): """Return the full message - @mess_idx: message index""" + @param 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 + @param mess_idx: message unique identifier""" + return self.mailbox.getMessage(self.getIdxFromUid(mess_uid)) + + def getFlags(self, mess_idx): + """Return the flags of the message + @param mess_idx: message index + @return: list of strings""" + id = self.getId(mess_idx) + return self.maildir.getFlags(self.name, id) + + def getFlagsUid(self, mess_uid): + """Return the flags of the message + @param mess_uid: message unique identifier + @return: list of strings""" + id = self.maildir.getIdFromUid(self.name,mess_uid) + return self.maildir.getFlags(self.name, id) + + def setFlags(self, mess_idx, flags): + """Change the flags of the message + @param mess_idx: message index + @param flags: list of strings + """ + id = self.getId(mess_idx) + return self.maildir.setFlags(self.name, id, flags) + + def setFlagsUid(self, mess_uid, flags): + """Change the flags of the message + @param mess_uid: message unique identifier + @param flags: list of strings + """ + id = self.maildir.getIdFromUid(self.name,mess_uid) + return self.maildir.setFlags(self.name, id, flags) + + def getMessageIdsWithFlag(self, flag): + """Return ids of messages where a flag is set + @param flag: flag to check + @return: list of id (as given by MaildirMailbox)""" + return self.maildir.getMessageIdsWithFlag(self.name,flag) + + def removeDeleted(self): + """Actually delete message flagged "\\Deleted" + Also purge the internal data of these messages + """ + for mess_id in self.getMessageIdsWithFlag("\\Deleted"): + print ("Deleting %s", mess_id) + self.mailbox.deleteMessage(self.getIdxFromId(mess_id)) + self.mailbox = maildir.MaildirMailbox(self.mailbox_path) #We need to reparse the dir to have coherent indexing + self.maildir.purgeDeleted(self.name) + + def emptyTrash(self): + """Delete everything in the .Trash dir""" + import shutils + pdb.set_trace() +