Browse IMAP folders in service discorvery (IMAP connection not yet implemented)

darcs-hash:20071011204051-86b55-b2e67101f0396919e907fe98c42b9034bc3142d7.gz
This commit is contained in:
David Rousselie
2007-10-11 22:40:51 +02:00
parent 7f4f8355aa
commit 95564d826e
3 changed files with 454 additions and 7 deletions

View File

@@ -25,14 +25,22 @@ import logging
from pyxmpp.jid import JID
from sqlobject.sqlbuilder import AND
import jcl.jabber as jabber
import jcl.model as model
from jcl.model import account
from jcl.model.account import PresenceAccount
from jcl.jabber.disco import RootDiscoGetInfoHandler
from jcl.model.account import Account, User, PresenceAccount
from jcl.jabber.disco import \
AccountTypeDiscoGetInfoHandler, AccountDiscoGetInfoHandler
from jcl.jabber.feeder import FeederComponent, Feeder, MessageSender, \
HeadlineSender, FeederHandler
from jcl.jabber.command import CommandRootDiscoGetInfoHandler
from jcl.jabber.component import AccountManager
from jmc.jabber.disco import MailRootDiscoGetInfoHandler
from jmc.jabber.disco import MailRootDiscoGetInfoHandler, \
IMAPAccountDiscoGetItemsHandler, MailAccountTypeDiscoGetInfoHandler, \
IMAPAccountDiscoGetInfoHandler
from jmc.jabber.message import SendMailMessageHandler, \
RootSendMailMessageHandler
from jmc.jabber.presence import MailSubscribeHandler, \
@@ -41,6 +49,47 @@ from jmc.model.account import MailAccount, IMAPAccount, POP3Account, \
SMTPAccount
from jmc.lang import Lang
class MailAccountManager(AccountManager):
def account_get_register(self, info_query,
name,
from_jid,
account_type,
lang_class):
"""Handle get_register on an IMAP account.
Return a preinitialized form.
account_type contains 'account_type + imap_dir'"""
splitted_node = account_type.split("/")
splitted_node_len = len(splitted_node)
if splitted_node_len == 1 or \
splitted_node[0] != "IMAP":
return AccountManager.account_get_register(self,
info_query,
name, from_jid,
account_type,
lang_class)
else:
info_query = info_query.make_result_response()
model.db_connect()
imap_dir = "/".join(splitted_node[1:])
bare_from_jid = from_jid.bare()
_account = account.get_account_filter(\
AND(AND(User.q.jid == unicode(bare_from_jid),
Account.q.userID == User.q.id),
IMAPAccount.q.mailbox == imap_dir),
account_class=IMAPAccount)
query = info_query.new_query("jabber:iq:register")
if _account is not None:
self.generate_registration_form_init(lang_class,
_account).as_xml(query)
else:
result = self.generate_registration_form(\
lang_class,
IMAPAccount,
bare_from_jid)
result["mailbox"].value = imap_dir
result.as_xml(query)
return [info_query]
class MailComponent(FeederComponent):
"""Jabber Mail Component main implementation"""
@@ -51,7 +100,8 @@ class MailComponent(FeederComponent):
port,
config,
config_file,
lang=Lang()):
lang=Lang(),
account_manager_class=MailAccountManager):
"""Use FeederComponent behavior and setup feeder and sender
attributes.
"""
@@ -62,7 +112,8 @@ class MailComponent(FeederComponent):
port,
config,
config_file,
lang=lang)
lang=lang,
account_manager_class=account_manager_class)
self.handler = MailFeederHandler(MailFeeder(self), MailSender(self))
self.account_manager.account_classes = (IMAPAccount,
POP3Account,
@@ -73,9 +124,20 @@ class MailComponent(FeederComponent):
self.presence_unsubscribe_handlers += [[MailUnsubscribeHandler(self)]]
self.presence_available_handlers += [[MailPresenceHandler(self)]]
self.presence_unavailable_handlers += [[MailPresenceHandler(self)]]
self.disco_get_items_handlers[0] += [IMAPAccountDiscoGetItemsHandler(self)]
jabber.replace_handlers(self.disco_get_info_handlers,
RootDiscoGetInfoHandler,
CommandRootDiscoGetInfoHandler,
MailRootDiscoGetInfoHandler(self))
jabber.replace_handlers(self.disco_get_info_handlers,
AccountTypeDiscoGetInfoHandler,
MailAccountTypeDiscoGetInfoHandler(self))
jabber.replace_handlers(self.disco_get_info_handlers,
AccountDiscoGetInfoHandler,
IMAPAccountDiscoGetInfoHandler(self))
# for hg in self.disco_get_items_handlers:
# print "----"
# for h in hg:
# print str(h)
class MailFeeder(Feeder):
"""Email check"""

View File

@@ -20,7 +20,18 @@
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
import logging
from pyxmpp.jid import JID
from pyxmpp.jabber.disco import DiscoItems, DiscoItem
from jcl import jabber
from jcl.model import account
from jcl.jabber.command import CommandRootDiscoGetInfoHandler
from jcl.jabber.disco import AccountTypeDiscoGetInfoHandler, \
AccountDiscoGetInfoHandler, DiscoHandler
from jmc.model.account import IMAPAccount
class MailRootDiscoGetInfoHandler(CommandRootDiscoGetInfoHandler):
def handle(self, stanza, lang_class, node, disco_obj, data):
@@ -30,3 +41,89 @@ class MailRootDiscoGetInfoHandler(CommandRootDiscoGetInfoHandler):
disco_infos[0].add_feature("jabber:iq:gateway")
disco_infos[0].add_identity(self.component.name, "headline", "newmail")
return disco_infos
class MailAccountTypeDiscoGetInfoHandler(AccountTypeDiscoGetInfoHandler):
def handle(self, stanza, lang_class, node, disco_obj, data):
disco_infos = AccountTypeDiscoGetInfoHandler.handle(self, stanza,
lang_class,
node, disco_obj,
data)
if node == "IMAP" and account.get_accounts(stanza.get_from().bare(),
IMAPAccount).count() > 0:
disco_infos[0].add_feature("http://jabber.org/protocol/disco#info")
disco_infos[0].add_feature("http://jabber.org/protocol/disco#items")
return disco_infos
class IMAPAccountDiscoGetInfoHandler(AccountDiscoGetInfoHandler):
def handle(self, stanza, lang_class, node, disco_obj, data):
disco_infos = AccountDiscoGetInfoHandler.handle(self, stanza,
lang_class,
node, disco_obj,
data)
splitted_node = node.split("/")
splitted_node_len = len(splitted_node)
if splitted_node_len > 1 and \
splitted_node[0] == "IMAP":
_account = account.get_account(stanza.get_from().bare(),
splitted_node[1],
IMAPAccount)
if _account is not None:
disco_infos[0].add_feature("http://jabber.org/protocol/disco#info")
if splitted_node_len > 2:
imap_dir = "/".join(splitted_node[2:])
if len(_account.ls_dir(imap_dir)) > 0:
disco_infos[0].add_feature("http://jabber.org/protocol/disco#items")
else:
disco_infos[0].add_feature("http://jabber.org/protocol/disco#items")
return disco_infos
class IMAPAccountDiscoGetItemsHandler(DiscoHandler):
def __init__(self, component):
DiscoHandler.__init__(self, component)
self.__logger = logging.getLogger("jcl.jabber.IMAPAccountDiscoGetItemsHandler")
def filter(self, stanza, lang_class, node=None):
if node is None:
return None
splitted_node = node.split("/")
splitted_node_len = len(splitted_node)
if splitted_node_len < 2:
return None
account_type = splitted_node[0]
if account_type != "IMAP":
return None
account_name = splitted_node[1]
if splitted_node_len == 2:
imap_dir = None
else:
imap_dir = "/".join(splitted_node[2:])
return (account_type, account_name, imap_dir)
def handle(self, stanza, lang_class, node, disco_obj, data):
"""Discovery get_items on an IMAP account"""
account_type, account_name, imap_dir = data
bare_from_jid = stanza.get_from().bare()
self.__logger.debug("Listing " + str(imap_dir) + " IMAP directory")
account_class = self.component.account_manager.get_account_class(account_type)
_account = account.get_account(bare_from_jid, account_name,
account_class=account_class)
if account_class is not None and _account is not None:
disco_items = DiscoItems()
if imap_dir is None:
# TODO : test if INBOX really exist, is it possible to retrieve default dir from IMAP ?
DiscoItem(disco_items,
JID(unicode(_account.jid) + "/" + account_type
+ "/INBOX"),
account_type + "/" + account_name + "/INBOX",
"INBOX")
else:
for subdir in _account.ls_dir(imap_dir):
DiscoItem(disco_items,
JID(unicode(_account.jid) + "/" + account_type + \
"/" + imap_dir + "/" + subdir),
account_type + "/" + account_name + "/" + imap_dir
+ "/" + subdir,
subdir)
return [disco_items]
return []
# TODO : implement get_info on imap_Dir

View File

@@ -31,6 +31,7 @@ from sqlobject.dbconnection import TheURIOpener
from pyxmpp.presence import Presence
from pyxmpp.message import Message
from pyxmpp.iq import Iq
from jcl.tests import JCLTestCase
import jcl.model as model
@@ -143,9 +144,16 @@ class MockIMAPAccount(MockMailAccount, IMAPAccount):
IMAPAccount._init(self, *args, **kw)
MockMailAccount._init(self)
def ls_dir(self, imap_dir):
if imap_dir == "INBOX":
return ["dir1", "dir2"]
elif imap_dir == "INBOX/dir1":
return ["subdir1", "subdir2"]
return []
class MockPOP3Account(MockMailAccount, POP3Account):
def _init(self, *args, **kw):
IMAPAccount._init(self, *args, **kw)
POP3Account._init(self, *args, **kw)
MockMailAccount._init(self)
class MockSMTPAccount(object):
@@ -514,6 +522,286 @@ class MailComponent_TestCase(JCLTestCase):
self.assertTrue(account11.marked_all_as_read)
model.db_disconnect()
def test_disco_get_info_imap_node(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
account11 = MockIMAPAccount(user=User(jid="user1@test.com"),
name="account11",
jid="account11@jmc.test.com")
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="jcl.test.com/IMAP")
disco_info = self.comp.disco_get_info("IMAP", info_query)
self.assertEquals(len(self.comp.stream.sent), 0)
self.assertTrue(disco_info.has_feature("jabber:iq:register"))
self.assertTrue(disco_info.has_feature("http://jabber.org/protocol/disco#info"))
self.assertTrue(disco_info.has_feature("http://jabber.org/protocol/disco#items"))
def test_disco_get_info_imap_node_no_account(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
account11 = MockPOP3Account(user=User(jid="user1@test.com"),
name="account11",
jid="account11@jmc.test.com")
account21 = MockIMAPAccount(user=User(jid="user2@test.com"),
name="account21",
jid="account21@jmc.test.com")
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="jcl.test.com/IMAP")
disco_info = self.comp.disco_get_info("IMAP", info_query)
self.assertEquals(len(self.comp.stream.sent), 0)
self.assertTrue(disco_info.has_feature("jabber:iq:register"))
self.assertFalse(disco_info.has_feature("http://jabber.org/protocol/disco#info"))
self.assertFalse(disco_info.has_feature("http://jabber.org/protocol/disco#items"))
def test_disco_get_info_imap_long_node(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
account11 = MockIMAPAccount(user=User(jid="user1@test.com"),
name="account11",
jid="account11@jmc.test.com")
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="account11@jcl.test.com/IMAP")
disco_info = self.comp.disco_get_info("IMAP/account11",
info_query)
self.assertEquals(len(self.comp.stream.sent), 0)
self.assertTrue(disco_info.has_feature("jabber:iq:register"))
self.assertTrue(disco_info.has_feature("http://jabber.org/protocol/disco#info"))
self.assertTrue(disco_info.has_feature("http://jabber.org/protocol/disco#items"))
def test_disco_get_info_not_imap_long_node(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
account11 = MockPOP3Account(user=User(jid="user1@test.com"),
name="account11",
jid="account11@jmc.test.com")
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="account11@jcl.test.com/POP3")
disco_info = self.comp.disco_get_info("POP3/account11",
info_query)
self.assertEquals(len(self.comp.stream.sent), 0)
self.assertTrue(disco_info.has_feature("jabber:iq:register"))
self.assertFalse(disco_info.has_feature("http://jabber.org/protocol/disco#info"))
self.assertFalse(disco_info.has_feature("http://jabber.org/protocol/disco#items"))
def test_disco_get_info_imap_dir_node(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
account11 = MockIMAPAccount(user=User(jid="user1@test.com"),
name="account11",
jid="account11@jmc.test.com")
account11.mailbox = "INBOX/dir1"
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="account11@jcl.test.com/IMAP/INBOX")
disco_info = self.comp.disco_get_info("IMAP/account11/INBOX",
info_query)
self.assertEquals(len(self.comp.stream.sent), 0)
self.assertTrue(disco_info.has_feature("jabber:iq:register"))
self.assertTrue(disco_info.has_feature("http://jabber.org/protocol/disco#info"))
self.assertTrue(disco_info.has_feature("http://jabber.org/protocol/disco#items"))
def test_disco_get_info_imap_dir_node_already_registered(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
account11 = MockIMAPAccount(user=User(jid="user1@test.com"),
name="account11",
jid="account11@jmc.test.com")
account11.mailbox = "INBOX"
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="account11@jcl.test.com/IMAP/INBOX")
disco_info = self.comp.disco_get_info("IMAP/account11/INBOX",
info_query)
self.assertEquals(len(self.comp.stream.sent), 0)
self.assertTrue(disco_info.has_feature("jabber:iq:register"))
self.assertTrue(disco_info.has_feature("http://jabber.org/protocol/disco#info"))
self.assertTrue(disco_info.has_feature("http://jabber.org/protocol/disco#items"))
def test_disco_get_info_imap_dir_node_last_subdir(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
account11 = MockIMAPAccount(user=User(jid="user1@test.com"),
name="account11",
jid="account11@jmc.test.com")
account11.mailbox = "INBOX/dir1/subdir1"
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="account11@jcl.test.com/IMAP/INBOX/dir1/subdir1")
disco_info = self.comp.disco_get_info("IMAP/account11/INBOX/dir1/subdir1",
info_query)
self.assertEquals(len(self.comp.stream.sent), 0)
self.assertTrue(disco_info.has_feature("jabber:iq:register"))
self.assertTrue(disco_info.has_feature("http://jabber.org/protocol/disco#info"))
self.assertFalse(disco_info.has_feature("http://jabber.org/protocol/disco#items"))
def test_disco_get_items_base_imap_account(self):
account11 = MockIMAPAccount(user=User(jid="user1@test.com"),
name="account11",
jid="account11@jcl.test.com")
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="account11@jcl.test.com/IMAP")
disco_items = self.comp.disco_get_items("IMAP/account11", info_query)
self.assertEquals(len(disco_items.get_items()), 1)
disco_item = disco_items.get_items()[0]
self.assertEquals(unicode(disco_item.get_jid()), unicode(account11.jid)
+ "/IMAP/INBOX")
self.assertEquals(disco_item.get_node(), "IMAP/" + account11.name + "/INBOX")
self.assertEquals(disco_item.get_name(), "INBOX")
def test_disco_get_items_inbox_imap_account(self):
account11 = MockIMAPAccount(user=User(jid="user1@test.com"),
name="account11",
jid="account11@jcl.test.com")
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="account11@jcl.test.com/IMAP/INBOX")
disco_items = self.comp.disco_get_items("IMAP/account11/INBOX", info_query)
self.assertEquals(len(disco_items.get_items()), 2)
disco_item = disco_items.get_items()[0]
self.assertEquals(unicode(disco_item.get_jid()), unicode(account11.jid)
+ "/IMAP/INBOX/dir1")
self.assertEquals(disco_item.get_node(), "IMAP/" + account11.name + "/INBOX"
+ "/dir1")
self.assertEquals(disco_item.get_name(), "dir1")
disco_item = disco_items.get_items()[1]
self.assertEquals(unicode(disco_item.get_jid()), unicode(account11.jid)
+ "/IMAP/INBOX/dir2")
self.assertEquals(disco_item.get_node(), "IMAP/" + account11.name + "/INBOX"
+ "/dir2")
self.assertEquals(disco_item.get_name(), "dir2")
def test_disco_get_items_subdir_imap_account(self):
account11 = MockIMAPAccount(user=User(jid="user1@test.com"),
name="account11",
jid="account11@jcl.test.com")
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="account11@jcl.test.com/IMAP")
disco_items = self.comp.disco_get_items("IMAP/account11/INBOX/dir1", info_query)
self.assertEquals(len(disco_items.get_items()), 2)
disco_item = disco_items.get_items()[0]
self.assertEquals(unicode(disco_item.get_jid()), unicode(account11.jid)
+ "/IMAP/INBOX/dir1/subdir1")
self.assertEquals(disco_item.get_node(), "IMAP/" + account11.name + "/INBOX"
+ "/dir1/subdir1")
self.assertEquals(disco_item.get_name(), "subdir1")
disco_item = disco_items.get_items()[1]
self.assertEquals(unicode(disco_item.get_jid()), unicode(account11.jid)
+ "/IMAP/INBOX/dir1/subdir2")
self.assertEquals(disco_item.get_node(), "IMAP/" + account11.name + "/INBOX"
+ "/dir1/subdir2")
self.assertEquals(disco_item.get_name(), "subdir2")
def test_account_get_register_imap_dir_already_registered(self):
model.db_connect()
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
user1 = User(jid="user1@test.com")
account1 = MockIMAPAccount(user=user1,
name="account1",
jid="account1@jcl.test.com")
account1.mailbox = "INBOX"
account11 = MockIMAPAccount(user=user1,
name="account11",
jid="account11@jcl.test.com")
account11.mailbox = "INBOX/dir1"
account21 = MockIMAPAccount(user=User(jid="user2@test.com"),
name="account21",
jid="account21@jcl.test.com")
model.db_disconnect()
self.comp.handle_get_register(Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="account1@jcl.test.com/IMAP/INBOX/dir1"))
self.assertEquals(len(self.comp.stream.sent), 1)
iq_sent = self.comp.stream.sent[0]
self.assertEquals(iq_sent.get_to(), "user1@test.com")
titles = iq_sent.xpath_eval("jir:query/jxd:x/jxd:title",
{"jir" : "jabber:iq:register",
"jxd" : "jabber:x:data"})
self.assertEquals(len(titles), 1)
self.assertEquals(titles[0].content,
Lang.en.register_title)
instructions = iq_sent.xpath_eval("jir:query/jxd:x/jxd:instructions",
{"jir" : "jabber:iq:register",
"jxd" : "jabber:x:data"})
self.assertEquals(len(instructions), 1)
self.assertEquals(instructions[0].content,
Lang.en.register_instructions)
fields = iq_sent.xpath_eval("jir:query/jxd:x/jxd:field",
{"jir" : "jabber:iq:register",
"jxd" : "jabber:x:data"})
self.assertEquals(len(fields), 16)
field = fields[0]
self.assertEquals(field.prop("type"), "hidden")
self.assertEquals(field.prop("var"), "name")
self.assertEquals(field.prop("label"), Lang.en.account_name)
self.assertEquals(field.children.name, "value")
self.assertEquals(field.children.content, "account11")
self.assertEquals(field.children.next.name, "required")
field = fields[15]
self.assertEquals(field.prop("type"), "text-single")
self.assertEquals(field.prop("var"), "mailbox")
self.assertEquals(field.prop("label"), Lang.en.field_mailbox)
self.assertEquals(field.children.name, "value")
self.assertEquals(field.children.content, "INBOX/dir1")
def test_account_get_register_imap_dir_new(self):
model.db_connect()
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
user1 = User(jid="user1@test.com")
account1 = MockIMAPAccount(user=user1,
name="account1",
jid="account1@jcl.test.com")
account1.maildir = "INBOX"
account11 = MockIMAPAccount(user=user1,
name="account11",
jid="account11@jcl.test.com")
account11.maildir = "INBOX/dir1"
account21 = MockIMAPAccount(user=User(jid="user2@test.com"),
name="account21",
jid="account21@jcl.test.com")
model.db_disconnect()
self.comp.handle_get_register(Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="account1@jcl.test.com/IMAP/INBOX/dir1/subdir1"))
self.assertEquals(len(self.comp.stream.sent), 1)
iq_sent = self.comp.stream.sent[0]
self.assertEquals(iq_sent.get_to(), "user1@test.com")
titles = iq_sent.xpath_eval("jir:query/jxd:x/jxd:title",
{"jir" : "jabber:iq:register",
"jxd" : "jabber:x:data"})
self.assertEquals(len(titles), 1)
self.assertEquals(titles[0].content,
Lang.en.register_title)
instructions = iq_sent.xpath_eval("jir:query/jxd:x/jxd:instructions",
{"jir" : "jabber:iq:register",
"jxd" : "jabber:x:data"})
self.assertEquals(len(instructions), 1)
self.assertEquals(instructions[0].content,
Lang.en.register_instructions)
fields = iq_sent.xpath_eval("jir:query/jxd:x/jxd:field",
{"jir" : "jabber:iq:register",
"jxd" : "jabber:x:data"})
self.assertEquals(len(fields), 16)
field = fields[0]
self.assertEquals(field.prop("type"), "text-single")
self.assertEquals(field.prop("var"), "name")
self.assertEquals(field.prop("label"), Lang.en.account_name)
self.assertEquals(field.children.name, "required")
self.assertEquals(field.children.next, None)
field = fields[15]
self.assertEquals(field.prop("type"), "text-single")
self.assertEquals(field.prop("var"), "mailbox")
self.assertEquals(field.prop("label"), Lang.en.field_mailbox)
self.assertEquals(field.children.name, "value")
self.assertEquals(field.children.content, "INBOX/dir1/subdir1")
class SendMailMessageHandler_TestCase(unittest.TestCase):
def setUp(self):
self.handler = SendMailMessageHandler(None)