plugin IMAP, plugin Maildir: profile management
- IMAP Login/pass is now checked against profile name/jabber pass - Mailboxes are now per-profile - flags are now checked without case sensitiveness
This commit is contained in:
parent
f77a660fe3
commit
bf514891c2
3 changed files with 159 additions and 99 deletions
|
|
@ -2,7 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
SAT plugin for managing imap server
|
||||
SàT plugin for managing imap server
|
||||
Copyright (C) 2011 Jérôme Poisson (goffi@goffi.org)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
|
@ -21,9 +21,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
from logging import debug, info, error
|
||||
import warnings
|
||||
from twisted.internet import protocol
|
||||
from twisted.internet import protocol,defer
|
||||
from twisted.words.protocols.jabber import error as jab_error
|
||||
from twisted.cred import portal,checkers
|
||||
from twisted.cred import portal,checkers,credentials
|
||||
from twisted.cred import error as cred_error
|
||||
from twisted.mail import imap4
|
||||
from email.parser import Parser
|
||||
import email.message
|
||||
|
|
@ -86,7 +87,6 @@ class Message():
|
|||
"""Retrieve the unique identifier associated with this message.
|
||||
"""
|
||||
debug('getUID (message)')
|
||||
debug ('===>%i', self.uid)
|
||||
return self.uid
|
||||
|
||||
def getFlags(self):
|
||||
|
|
@ -152,14 +152,13 @@ class Message():
|
|||
class SatMailbox:
|
||||
implements(imap4.IMailbox)
|
||||
|
||||
def __init__(self,host,name):
|
||||
def __init__(self,host,name,profile):
|
||||
self.host = host
|
||||
self.listeners=set()
|
||||
debug ('Mailbox init (%s)', name)
|
||||
if name!="INBOX":
|
||||
raise imap4.MailboxException("Only INBOX is managed for the moment")
|
||||
self.name=name
|
||||
self.mailbox=self.host.plugins["Maildir"].accessMessageBox("INBOX",self.newMessage)
|
||||
self.mailbox=self.host.plugins["Maildir"].accessMessageBox(name,self.newMessage, profile)
|
||||
|
||||
def newMessage(self):
|
||||
"""Called when a new message is in the mailbox"""
|
||||
|
|
@ -207,7 +206,7 @@ class SatMailbox:
|
|||
"""Return the number of messages with the 'Unseen' flag.
|
||||
"""
|
||||
debug('getUnseenCount')
|
||||
return self.getMessageCount()-len(self.mailbox.getMessageIdsWithFlag('\\Seen'))
|
||||
return self.getMessageCount()-len(self.mailbox.getMessageIdsWithFlag('\\SEEN'))
|
||||
|
||||
def isWriteable(self):
|
||||
"""Get the read/write status of the mailbox.
|
||||
|
|
@ -271,7 +270,7 @@ class SatMailbox:
|
|||
invoked otherwise.
|
||||
"""
|
||||
debug('addMessage')
|
||||
raise NotImplementedError
|
||||
raise imap4.MailboxException("Client message addition not implemented yet")
|
||||
|
||||
def expunge(self):
|
||||
"""Remove all messages flagged \\Deleted.
|
||||
|
|
@ -323,6 +322,8 @@ class SatMailbox:
|
|||
"""
|
||||
debug('store')
|
||||
|
||||
flags=[flag.upper() for flag in flags]
|
||||
|
||||
def updateFlags(getF,setF):
|
||||
ret = {}
|
||||
for mess_id in messages:
|
||||
|
|
@ -335,16 +336,27 @@ class SatMailbox:
|
|||
_flags.update(set(flags))
|
||||
new_flags=list(_flags)
|
||||
setF(mess_id, new_flags)
|
||||
ret[mess_id] = new_flags
|
||||
ret[mess_id] = tuple(new_flags)
|
||||
return ret
|
||||
|
||||
if uid:
|
||||
messages.last = self.mailbox.getMaxUid()
|
||||
messages.getnext = self.mailbox.getNextExistingUid
|
||||
return updateFlags(self.mailbox.getFlagsUid,self.mailbox.setFlagsUid)
|
||||
ret = updateFlags(self.mailbox.getFlagsUid,self.mailbox.setFlagsUid)
|
||||
for listener in self.listeners:
|
||||
listener.flagsChanged(ret)
|
||||
return ret
|
||||
|
||||
else:
|
||||
messages.last = self.getMessageCount()
|
||||
return updateFlags(self.mailbox.getFlags,self.mailbox.setFlags)
|
||||
ret = updateFlags(self.mailbox.getFlags,self.mailbox.setFlags)
|
||||
newFlags={}
|
||||
for idx in ret:
|
||||
#we have to convert idx to uid for the listeners
|
||||
newFlags[self.mailbox.getUid(idx)] = ret[idx]
|
||||
for listener in self.listeners:
|
||||
listener.flagsChanged(newFlags)
|
||||
return ret
|
||||
|
||||
def getFlags(self):
|
||||
"""Return the flags defined in this mailbox
|
||||
|
|
@ -352,7 +364,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'] #TODO: add '\Recent'
|
||||
return ['\\SEEN','\\ANSWERED','\\FLAGGED','\\DELETED','\\DRAFT'] #TODO: add '\\RECENT'
|
||||
|
||||
def getHierarchicalDelimiter(self):
|
||||
"""Get the character which delimits namespaces for in this mailbox.
|
||||
|
|
@ -360,21 +372,19 @@ class SatMailbox:
|
|||
debug('getHierarchicalDelimiter')
|
||||
return '.'
|
||||
|
||||
|
||||
|
||||
class ImapAccount(imap4.MemoryAccount):
|
||||
class ImapSatAccount(imap4.MemoryAccount):
|
||||
#implements(imap4.IAccount)
|
||||
# Actually implement the interface here
|
||||
|
||||
def __init__(self, host, name):
|
||||
def __init__(self, host, profile):
|
||||
debug("ImapAccount init")
|
||||
self.host=host
|
||||
imap4.MemoryAccount.__init__(self,name)
|
||||
self.profile=profile
|
||||
imap4.MemoryAccount.__init__(self,profile)
|
||||
self.addMailbox("Inbox") #We only manage Inbox for the moment
|
||||
debug ('INBOX added')
|
||||
|
||||
def _emptyMailbox(self, name, id):
|
||||
return SatMailbox(self.host,name)
|
||||
return SatMailbox(self.host,name,self.profile)
|
||||
|
||||
|
||||
class ImapRealm:
|
||||
|
|
@ -385,9 +395,40 @@ class ImapRealm:
|
|||
|
||||
def requestAvatar(self, avatarID, mind, *interfaces):
|
||||
debug('requestAvatar')
|
||||
profile=avatarID.decode('utf-8')
|
||||
if imap4.IAccount not in interfaces:
|
||||
raise NotImplementedError
|
||||
return imap4.IAccount, ImapAccount(self.host,avatarID), lambda:None
|
||||
return imap4.IAccount, ImapSatAccount(self.host,profile), lambda:None
|
||||
|
||||
class SatProfileCredentialChecker:
|
||||
"""
|
||||
This credential checker check against SàT's profile and associated jabber's password
|
||||
Check if the profile exists, and if the password is OK
|
||||
Return the profile as avatarId
|
||||
"""
|
||||
implements(checkers.ICredentialsChecker)
|
||||
credentialInterfaces = (credentials.IUsernamePassword,
|
||||
credentials.IUsernameHashedPassword)
|
||||
|
||||
|
||||
def __init__(self, host):
|
||||
self.host = host
|
||||
|
||||
def _cbPasswordMatch(self, matched, profile):
|
||||
if matched:
|
||||
return profile.encode('utf-8')
|
||||
else:
|
||||
return failure.Failure(cred_error.UnauthorizedLogin())
|
||||
|
||||
def requestAvatarId(self, credentials):
|
||||
profiles = self.host.memory.getProfilesList()
|
||||
if not credentials.username in profiles:
|
||||
return defer.fail(cred_error.UnauthorizedLogin())
|
||||
password = self.host.memory.getParamA("Password", "Connection", profile_key=credentials.username)
|
||||
return defer.maybeDeferred(
|
||||
credentials.checkPassword,
|
||||
password).addCallback(
|
||||
self._cbPasswordMatch, credentials.username)
|
||||
|
||||
class ImapServerFactory(protocol.ServerFactory):
|
||||
protocol = imap4.IMAP4Server
|
||||
|
|
@ -405,5 +446,5 @@ class ImapServerFactory(protocol.ServerFactory):
|
|||
debug ("Building protocole")
|
||||
prot = protocol.ServerFactory.buildProtocol(self, addr)
|
||||
prot.portal = portal.Portal(ImapRealm(self.host))
|
||||
prot.portal.registerChecker(checkers.InMemoryUsernamePasswordDatabaseDontUse(goffi="toto"))
|
||||
prot.portal.registerChecker(SatProfileCredentialChecker(self.host))
|
||||
return prot
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
SAT plugin for managing imap server
|
||||
SàT plugin for managing Maildir type mail boxes
|
||||
Copyright (C) 2011 Jérôme Poisson (goffi@goffi.org)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
|
@ -33,6 +33,7 @@ import os,os.path
|
|||
from cStringIO import StringIO
|
||||
from twisted.internet import reactor
|
||||
import pdb
|
||||
from sat.exceptions import *
|
||||
|
||||
|
||||
from zope.interface import implements
|
||||
|
|
@ -61,8 +62,9 @@ class MaildirBox():
|
|||
self.host = host
|
||||
|
||||
self.__observed={}
|
||||
self.__mailboxes={}
|
||||
self.data=host.memory.getPrivate("MAILDIR_data") or {"INBOX":{"cur_idx":0}}
|
||||
pList=host.memory.getProfilesList #shorter :)
|
||||
self.__mailboxes=dict(zip(pList(),len(pList())*[{}]))
|
||||
self.data=host.memory.getPrivate("MAILDIR_data") or dict(zip(pList(),len(pList())*[{"INBOX":{"cur_idx":0}}])) #Create empty box for each profile
|
||||
#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, ...])
|
||||
|
|
@ -74,18 +76,26 @@ class MaildirBox():
|
|||
debug('Destroying MaildirBox')
|
||||
self.host.memory.setPrivate('MAILDIR_data',self.data)
|
||||
|
||||
def accessMessageBox(self, boxname, observer=None):
|
||||
def accessMessageBox(self, boxname, observer=None, profile_key='@DEFAULT@'):
|
||||
"""Create and return a MailboxUser instance
|
||||
@param boxname: name of the box
|
||||
@param observer: method to call when a NewMessage arrive"""
|
||||
if not self.__mailboxes.has_key(boxname):
|
||||
self.__mailboxes[boxname]=MailboxUser(self, boxname, observer)
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not profile:
|
||||
raise ProfileUnknownError
|
||||
if not self.__mailboxes[profile].has_key(boxname):
|
||||
self.__mailboxes[profile][boxname]=MailboxUser(self, boxname, observer, profile=profile)
|
||||
else:
|
||||
if observer:
|
||||
self.addObserver(observer, boxname)
|
||||
return self.__mailboxes[boxname]
|
||||
self.addObserver(observer, profile, boxname)
|
||||
return self.__mailboxes[profile][boxname]
|
||||
|
||||
def _removeBoxAccess(self, boxname, mailboxUser):
|
||||
def _getProfilePath(self, profile):
|
||||
"""Return a unique path for profile's mailbox
|
||||
The path must be unique, usable as a dir name, and bijectional"""
|
||||
return profile.replace('/','_').replace('..','_') #FIXME: this is too naive to work well, must be improved
|
||||
|
||||
def _removeBoxAccess(self, boxname, mailboxUser, profile):
|
||||
"""Remove a reference to a box
|
||||
@param name: name of the box
|
||||
@param mailboxUser: MailboxUser instance"""
|
||||
|
|
@ -93,32 +103,33 @@ class MaildirBox():
|
|||
err_msg=_("Trying to remove an mailboxUser not referenced")
|
||||
error(_("INTERNAL ERROR: ") + err_msg)
|
||||
raise MaildirError(err_msg)
|
||||
assert self.__mailboxes[boxname]==mailboxUser
|
||||
del __mailboxes[boxname]
|
||||
assert self.__mailboxes[profile][boxname]==mailboxUser
|
||||
del __mailboxes[profile][boxname]
|
||||
|
||||
def _checkBoxReference(self, boxname):
|
||||
def _checkBoxReference(self, boxname, profile):
|
||||
"""Check if there is a reference on a box, and return it
|
||||
@param boxname: name of the box to check
|
||||
@return: MailboxUser instance or None"""
|
||||
if self.__mailboxes.has_key(boxname):
|
||||
return self.__mailboxes[boxname]
|
||||
if self.__mailboxes.has_key(profile):
|
||||
if self.__mailboxes[profile].has_key(boxname):
|
||||
return self.__mailboxes[profile][boxname]
|
||||
|
||||
def __getBoxData(self, boxname):
|
||||
def __getBoxData(self, boxname, profile):
|
||||
"""Return the date of a box"""
|
||||
try:
|
||||
return self.data[boxname] #the boxname MUST exist in the data
|
||||
return self.data[profile][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):
|
||||
def getUid(self, boxname, message_id, profile):
|
||||
"""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"""
|
||||
box_data = self.__getBoxData(boxname)
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
if box_data.has_key(message_id):
|
||||
ret = box_data[message_id][0]
|
||||
else:
|
||||
|
|
@ -128,40 +139,40 @@ class MaildirBox():
|
|||
self.host.memory.setPrivate('MAILDIR_data',self.data)
|
||||
return ret
|
||||
|
||||
def getNextUid(self, boxname):
|
||||
def getNextUid(self, boxname, profile):
|
||||
"""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"""
|
||||
box_data = self.__getBoxData(boxname)
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
return box_data['cur_idx']+1
|
||||
|
||||
def getNextExistingUid(self, boxname, uid):
|
||||
def getNextExistingUid(self, boxname, uid, profile):
|
||||
"""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"""
|
||||
box_data = self.__getBoxData(boxname)
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
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, profile) == 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):
|
||||
def getMaxUid(self, boxname, profile):
|
||||
"""Give the max existing uid
|
||||
@param boxname: name of the box where the message is
|
||||
@return: uid"""
|
||||
box_data = self.__getBoxData(boxname)
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
return box_data['cur_idx']
|
||||
|
||||
def getIdFromUid(self, boxname, message_uid):
|
||||
def getIdFromUid(self, boxname, message_uid, profile):
|
||||
"""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"""
|
||||
box_data = self.__getBoxData(boxname)
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
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
|
||||
|
|
@ -169,36 +180,38 @@ class MaildirBox():
|
|||
return message_id
|
||||
return None
|
||||
|
||||
def getFlags(self, boxname, mess_id):
|
||||
def getFlags(self, boxname, mess_id, profile):
|
||||
"""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)
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
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):
|
||||
def setFlags(self, boxname, mess_id, flags, profile):
|
||||
"""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)
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
assert(type(flags)==list)
|
||||
flags=[flag.upper() for flag in flags] #we store every flag UPPERCASE
|
||||
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):
|
||||
def getMessageIdsWithFlag(self, boxname, flag, profile):
|
||||
"""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)
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
assert(isinstance(flag,basestring))
|
||||
flag=flag.upper()
|
||||
result = []
|
||||
for key in box_data:
|
||||
if key=='cur_idx':
|
||||
|
|
@ -207,20 +220,20 @@ class MaildirBox():
|
|||
result.append(key)
|
||||
return result
|
||||
|
||||
def purgeDeleted(self, boxname):
|
||||
def purgeDeleted(self, boxname, profile):
|
||||
"""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"):
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
for mess_id in self.getMessageIdsWithFlag(boxname,"\\Deleted", profile):
|
||||
del(box_data[mess_id])
|
||||
self.host.memory.setPrivate('MAILDIR_data',self.data)
|
||||
|
||||
def cleanTable(self, boxname, existant_id):
|
||||
def cleanTable(self, boxname, existant_id, profile):
|
||||
"""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"""
|
||||
box_data = self.__getBoxData(boxname)
|
||||
box_data = self.__getBoxData(boxname, profile)
|
||||
to_remove=[]
|
||||
for key in box_data:
|
||||
if key not in existant_id and key!="cur_idx":
|
||||
|
|
@ -229,7 +242,7 @@ class MaildirBox():
|
|||
del box_data[key]
|
||||
|
||||
|
||||
def MessageReceivedTrigger(self, message):
|
||||
def MessageReceivedTrigger(self, message, profile):
|
||||
"""This trigger catch normal message and put the in the Maildir box.
|
||||
If the message is not of "normal" type, do nothing
|
||||
@param message: message xmlstrem
|
||||
|
|
@ -239,44 +252,44 @@ class MaildirBox():
|
|||
type = message['type'] if message.hasAttribute('type') else 'chat' #FIXME: check specs
|
||||
if message['type'] != 'normal':
|
||||
return True
|
||||
self.accessMessageBox("INBOX").addMessage(message)
|
||||
self.accessMessageBox("INBOX", profile_key=profile).addMessage(message)
|
||||
return False
|
||||
|
||||
def addObserver(self, callback, boxname, signal="NEW_MESSAGE"):
|
||||
def addObserver(self, callback, profile, boxname, signal="NEW_MESSAGE"):
|
||||
"""Add an observer for maildir box changes
|
||||
@param callback: method to call when the the box is updated
|
||||
@param boxname: name of the box to observe
|
||||
@param signal: which signal is observed by the caller"""
|
||||
if not self.__observed.has_key(boxname):
|
||||
self.__observed[boxname]={}
|
||||
if not self.__observed[boxname].has_key(signal):
|
||||
self.__observed[boxname][signal]=set()
|
||||
self.__observed[boxname][signal].add(callback)
|
||||
if not self.__observed.has_key((profile,boxname)):
|
||||
self.__observed[(profile,boxname)]={}
|
||||
if not self.__observed[(profile,boxname)].has_key(signal):
|
||||
self.__observed[(profile,boxname)][signal]=set()
|
||||
self.__observed[(profile,boxname)][signal].add(callback)
|
||||
|
||||
def removeObserver(self, callback, boxname, signal="NEW_MESSAGE"):
|
||||
def removeObserver(self, callback, profile, boxname, signal="NEW_MESSAGE"):
|
||||
"""Remove an observer of maildir box changes
|
||||
@param callback: method to remove from obervers
|
||||
@param boxname: name of the box which was observed
|
||||
@param signal: which signal was observed by the caller"""
|
||||
if not self.__observed.has_key(boxname):
|
||||
if not self.__observed.has_key((profile,boxname)):
|
||||
err_msg=_("Trying to remove an observer for an inexistant mailbox")
|
||||
error(_("INTERNAL ERROR: ") + err_msg)
|
||||
raise MaildirError(err_msg)
|
||||
if not self.__observed[boxname].has_key(signal):
|
||||
if not self.__observed[(profile,boxname)].has_key(signal):
|
||||
err_msg=_("Trying to remove an inexistant observer, no observer for this signal")
|
||||
error(_("INTERNAL ERROR: ") + err_msg)
|
||||
raise MaildirError(err_msg)
|
||||
if not callback in self.__observed[boxname][signal]:
|
||||
if not callback in self.__observed[(profile,boxname)][signal]:
|
||||
err_msg=_("Trying to remove an inexistant observer")
|
||||
error(_("INTERNAL ERROR: ") + err_msg)
|
||||
raise MaildirError(err_msg)
|
||||
self.__observed[boxname][signal].remove(callback)
|
||||
self.__observed[(profile,boxname)][signal].remove(callback)
|
||||
|
||||
def emitSignal(self, boxname, signal_name):
|
||||
def emitSignal(self, profile, boxname, signal_name):
|
||||
"""Emit the signal to observer"""
|
||||
debug('emitSignal %s %s' %(boxname, signal_name))
|
||||
debug('emitSignal %s %s %s' %(profile, boxname, signal_name))
|
||||
try:
|
||||
for observer_cb in self.__observed[boxname][signal_name]:
|
||||
for observer_cb in self.__observed[(profile, boxname)][signal_name]:
|
||||
observer_cb()
|
||||
except KeyError:
|
||||
pass
|
||||
|
|
@ -304,27 +317,32 @@ class MailboxUser:
|
|||
mail['Subject'] = e.children[0].encode('utf-8')
|
||||
return mail.as_string()
|
||||
|
||||
def __init__(self, _maildir, name, observer=None):
|
||||
def __init__(self, _maildir, name, observer=None, profile="@NONE@"):
|
||||
"""@param _maildir: the main MaildirBox instance
|
||||
@param name: name of the mailbox
|
||||
@param profile: real profile (ie not a profile_key)
|
||||
THIS OBJECT MUST NOT BE USED DIRECTLY: use MaildirBox.accessMessageBox instead"""
|
||||
if _maildir._checkBoxReference(self):
|
||||
if _maildir._checkBoxReference(name, profile):
|
||||
error ("INTERNAL ERROR: MailboxUser MUST NOT be instancied directly")
|
||||
raise MailboxException('double MailboxUser instanciation')
|
||||
if name!="INBOX":
|
||||
raise NotImplementedError
|
||||
self.name=name
|
||||
self.profile=profile
|
||||
self.maildir=_maildir
|
||||
mailbox_path = os.path.expanduser(os.path.join(self.maildir.host.get_const('local_dir'), MAILDIR_PATH))
|
||||
profile_path = self.maildir._getProfilePath(profile)
|
||||
full_profile_path = os.path.join(os.path.expanduser(self.maildir.host.get_const('local_dir')),profile_path)
|
||||
if not os.path.exists(full_profile_path):
|
||||
os.makedirs(full_profile_path,0700)
|
||||
mailbox_path = os.path.join(full_profile_path, MAILDIR_PATH)
|
||||
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")
|
||||
debug("adding observer for %s (%s)" % (name,profile))
|
||||
self.maildir.addObserver(observer, profile, name, "NEW_MESSAGE")
|
||||
|
||||
def __uid_table_update(self):
|
||||
existant_id=[]
|
||||
|
|
@ -332,14 +350,14 @@ class MailboxUser:
|
|||
#we update the uid table
|
||||
existant_id.append(self.getId(mess_idx))
|
||||
self.getUid(mess_idx)
|
||||
self.maildir.cleanTable(self.name, existant_id)
|
||||
self.maildir.cleanTable(self.name, existant_id, profile=self.profile)
|
||||
|
||||
|
||||
def __del__(self):
|
||||
if observer:
|
||||
debug("removing observer for %s" % self.name)
|
||||
self._maildir.removeObserver(observer, self.name, "NEW_MESSAGE")
|
||||
self._maildir._removeBoxAccess(self.name, self)
|
||||
self.maildir._removeBoxAccess(self.name, self, profile=self.profile)
|
||||
|
||||
def addMessage(self, message):
|
||||
"""Add a message to the box
|
||||
|
|
@ -348,8 +366,9 @@ class MailboxUser:
|
|||
|
||||
def emitSignal(self, ignore, signal):
|
||||
"""Emit the signal to the observers"""
|
||||
self.getUid(self.getMessageCount()-1) #we make an uid for the last message added
|
||||
self.maildir.emitSignal(self.name, signal)
|
||||
if signal=="NEW_MESSAGE":
|
||||
self.getUid(self.getMessageCount()-1) #XXX: we make an uid for the last message added
|
||||
self.maildir.emitSignal(self.profile, self.name, signal)
|
||||
|
||||
def getId(self, mess_idx):
|
||||
"""Return the Unique ID of the message
|
||||
|
|
@ -359,16 +378,16 @@ class MailboxUser:
|
|||
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)
|
||||
return self.maildir.getUid(self.name,mess_id, profile=self.profile)
|
||||
|
||||
def getNextUid(self):
|
||||
return self.maildir.getNextUid(self.name)
|
||||
return self.maildir.getNextUid(self.name, profile=self.profile)
|
||||
|
||||
def getNextExistingUid(self, uid):
|
||||
return self.maildir.getNextExistingUid(self.name, uid)
|
||||
return self.maildir.getNextExistingUid(self.name, uid, profile=self.profile)
|
||||
|
||||
def getMaxUid(self):
|
||||
return self.maildir.getMaxUid(self.name)
|
||||
return self.maildir.getMaxUid(self.name, profile=self.profile)
|
||||
|
||||
def getMessageCount(self):
|
||||
"""Return number of mails present in this box"""
|
||||
|
|
@ -412,14 +431,14 @@ class MailboxUser:
|
|||
@param mess_idx: message index
|
||||
@return: list of strings"""
|
||||
id = self.getId(mess_idx)
|
||||
return self.maildir.getFlags(self.name, id)
|
||||
return self.maildir.getFlags(self.name, id, profile=self.profile)
|
||||
|
||||
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)
|
||||
id = self.maildir.getIdFromUid(self.name,mess_uid, profile=self.profile)
|
||||
return self.maildir.getFlags(self.name, id, profile=self.profile)
|
||||
|
||||
def setFlags(self, mess_idx, flags):
|
||||
"""Change the flags of the message
|
||||
|
|
@ -427,21 +446,21 @@ class MailboxUser:
|
|||
@param flags: list of strings
|
||||
"""
|
||||
id = self.getId(mess_idx)
|
||||
return self.maildir.setFlags(self.name, id, flags)
|
||||
self.maildir.setFlags(self.name, id, flags, profile=self.profile)
|
||||
|
||||
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)
|
||||
id = self.maildir.getIdFromUid(self.name,mess_uid, profile=self.profile)
|
||||
return self.maildir.setFlags(self.name, id, flags, profile=self.profile)
|
||||
|
||||
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)
|
||||
return self.maildir.getMessageIdsWithFlag(self.name,flag, profile=self.profile)
|
||||
|
||||
def removeDeleted(self):
|
||||
"""Actually delete message flagged "\\Deleted"
|
||||
|
|
@ -451,7 +470,7 @@ class MailboxUser:
|
|||
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)
|
||||
self.maildir.purgeDeleted(self.name, profile=self.profile)
|
||||
|
||||
def emptyTrash(self):
|
||||
"""Delete everything in the .Trash dir"""
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ class SatMessageProtocol(xmppim.MessageProtocol):
|
|||
|
||||
def onMessage(self, message):
|
||||
debug (_(u"got message from: %s"), message["from"])
|
||||
if not self.host.trigger.point("MessageReceived",message):
|
||||
if not self.host.trigger.point("MessageReceived",message, profile=self.parent.profile):
|
||||
return
|
||||
for e in message.elements():
|
||||
if e.name == "body":
|
||||
|
|
|
|||
Loading…
Reference in a new issue