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
|
||||
|
|
|
|||
Loading…
Reference in a new issue