plugin IMAP, plugin Maildir: added flag, IMAP's uid management
This commit is contained in:
parent
e14f357b95
commit
f3565fa4a8
2 changed files with 175 additions and 63 deletions
|
|
@ -75,9 +75,10 @@ class IMAP_server():
|
|||
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 @@ class Message():
|
|||
@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 @@ class SatMailbox:
|
|||
@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 @@ class SatMailbox:
|
|||
"""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 @@ class SatMailbox:
|
|||
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 @@ class SatMailbox:
|
|||
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 @@ class SatMailbox:
|
|||
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 @@ class SatMailbox:
|
|||
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 @@ class SatMailbox:
|
|||
@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.
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class MaildirBox():
|
|||
#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 @@ class MaildirBox():
|
|||
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 @@ class MaildirBox():
|
|||
@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 @@ class MaildirBox():
|
|||
"""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 @@ class MaildirBox():
|
|||
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 @@ class MailboxUser:
|
|||
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 @@ class MailboxUser:
|
|||
"""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()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue