Basic Ad-Hoc commands support

Support commands listing (disco get items) and command info (disco get info)

darcs-hash:20070620164706-86b55-11fb94510d349169ab0450717a680db4440cd9b3.gz
This commit is contained in:
David Rousselie
2007-06-20 18:47:06 +02:00
parent f79c1ee4bf
commit c4daddd569
9 changed files with 177 additions and 76 deletions

View File

@@ -1,2 +1,29 @@
"""Jabber related classes"""
__revision__ = ""
from jcl.model.account import Account
class Handler(object):
"""handling class"""
def filter(self, stanza, lang_class):
"""Filter account to be processed by the handler
return all accounts. DB connection might already be opened."""
accounts = Account.select()
return accounts
def handle(self, stanza, lang_class, accounts):
"""Apply actions to do on given accounts
Do nothing by default"""
return []
class DiscoHandler(object):
"""Handle disco get items requests"""
def filter(self, node, info_query):
"""Filter requests to be handled"""
return False
def handle(self, disco_items, node, info_query, data, lang_class):
"""Handle disco get items request"""
return None

View File

@@ -50,7 +50,11 @@ from pyxmpp.presence import Presence
from pyxmpp.jabber.dataforms import Form, Field, Option
import jcl
from jcl.jabber.error import FieldError
from jcl.jabber import Handler
from jcl.error import FieldError
import jcl.jabber.command as command
from jcl.jabber.command import CommandDiscoGetItemsHandler, \
CommandDiscoGetInfoHandler, CommandManager, JCLCommandManager
from jcl.model import account
from jcl.model.account import Account, LegacyJID
from jcl.lang import Lang
@@ -98,6 +102,11 @@ class JCLComponent(Component, object):
self.unsubscribe_handlers = []
self.available_handlers = []
self.unavailable_handlers = []
command.command_manager = JCLCommandManager()
command.command_manager.component = self
command.command_manager.account_manager = self.account_manager
self.disco_get_items_handlers = [CommandDiscoGetItemsHandler()]
self.disco_get_info_handlers = [CommandDiscoGetInfoHandler()]
self.__logger = logging.getLogger("jcl.jabber.JCLComponent")
self.lang = lang
@@ -322,34 +331,55 @@ class JCLComponent(Component, object):
self.stream.send(info_query)
return 1
def disco_get_commands_list():
"""Return Ad-Hoc commands list"""
return None
def disco_get_info(self, node, info_query):
"""Discovery get info handler
"""
return self.apply_behavior(\
info_query,
lambda name, from_jid, account_type, lang_class: \
self.account_manager.account_disco_get_info(),
lambda name, from_jid, account_type, lang_class: \
self.account_manager.account_type_disco_get_info(),
lambda name, from_jid, account_type, lang_class: \
self.account_manager.root_disco_get_info(\
self.name,
self.disco_identity.category,
self.disco_identity.type))
result = self.apply_behavior(\
info_query,
lambda name, from_jid, account_type, lang_class: \
self.account_manager.account_disco_get_info(),
lambda name, from_jid, account_type, lang_class: \
self.account_manager.account_type_disco_get_info(),
lambda name, from_jid, account_type, lang_class: \
self.account_manager.root_disco_get_info(\
node,
self.name,
self.disco_identity.category,
self.disco_identity.type))
if result is None:
lang_class = self.lang.get_lang_class_from_node(info_query.get_node())
for disco_filter in self.disco_get_info_handlers:
data = disco_filter.filter(node, info_query)
if data:
result = disco_filter.handle(result, node, info_query,
data, lang_class)
return result
def disco_get_items(self, node, info_query):
"""Discovery get nested nodes handler
"""
return self.apply_behavior(\
result = self.apply_behavior(\
info_query,
lambda name, from_jid, account_type, lang_class: \
DiscoItems(),
None,
lambda name, from_jid, account_type, lang_class: \
self.account_manager.account_type_disco_get_items(from_jid,
account_type),
lambda name, from_jid, account_type, lang_class: \
self.account_manager.root_disco_get_items(from_jid,
lang_class))
if result is None:
lang_class = self.lang.get_lang_class_from_node(info_query.get_node())
for disco_filter in self.disco_get_items_handlers:
data = disco_filter.filter(node, info_query)
if data:
result = disco_filter.handle(result, node, info_query,
data, lang_class)
return result
def handle_get_version(self, info_query):
"""Get Version handler
@@ -569,8 +599,10 @@ class AccountManager(object):
def __init__(self, component):
"""AccountManager constructor"""
self.__logger = logging.getLogger("jcl.jabber.JCLComponent")
self.regexp_type = re.compile("(.*)Account$")
self.account_classes = (Account,)
self.component = component
self.account_types = []
def _get_account_classes(self):
"""account_classes getter"""
@@ -580,6 +612,16 @@ class AccountManager(object):
"""account_classes setter"""
self._account_classes = account_classes
self.has_multiple_account_type = (len(self._account_classes) > 1)
self.account_types = []
for account_class in account_classes:
match = self.regexp_type.search(account_class.__name__)
if match is not None:
account_type = match.group(1)
self.account_types.append(account_type)
else:
self.__logger.error(account_class.__name__ +
" name not well formed")
self.account_types.append("")
account_classes = property(_get_account_classes, _set_account_classes)
@@ -596,38 +638,38 @@ class AccountManager(object):
self.__logger.debug("account_type_disco_get_info")
return self.account_disco_get_info()
def root_disco_get_info(self, name, category, type):
def root_disco_get_info(self, node, name, category, type):
"""Implement discovery get_info on main component JID"""
self.__logger.debug("root_disco_get_info")
disco_info = DiscoInfo()
disco_info.add_feature("jabber:iq:version")
if not self.has_multiple_account_type:
disco_info.add_feature("jabber:iq:register")
DiscoIdentity(disco_info, name,
category,
type)
return disco_info
if not node:
disco_info = DiscoInfo()
disco_info.add_feature("jabber:iq:version")
if not self.has_multiple_account_type:
disco_info.add_feature("jabber:iq:register")
DiscoIdentity(disco_info, name,
category,
type)
return disco_info
else:
return None
###### disco_get_items handlers ######
def account_type_disco_get_items(self, from_jid, account_type):
"""Discovery get_items on an account type node"""
self.__logger.debug("Listing account for " + account_type)
disco_items = DiscoItems()
account_class = self._get_account_class(account_type + "Account")
if account_class is not None:
self._list_accounts(disco_items,
account_class,
from_jid.bare(),
account_type=account_type)
return self._list_accounts(account_class,
from_jid.bare(),
account_type=account_type)
else:
self.__logger.error("Error: " + account_class.__name__
+ " class not in account_classes")
return disco_items
return None
def root_disco_get_items(self, from_jid, lang_class):
"""Discovery get_items on root node"""
disco_items = DiscoItems()
regexp_type = re.compile("(.*)Account$")
disco_items = None
if self.has_multiple_account_type: # list accounts with only one type declared
def _list_account_types(disco_items, account_class, bare_from_jid, account_type):
type_label_attr = "type_" + account_type.lower() + "_name"
@@ -640,20 +682,15 @@ class AccountManager(object):
account_type),
account_type,
type_label)
list_func = _list_account_types
disco_items = DiscoItems()
for account_type in self.account_types:
_list_account_types(disco_items,
self._get_account_class(account_type
+ "Account"),
from_jid.bare(), account_type)
else:
list_func = self._list_accounts
for account_class in self.account_classes:
match = regexp_type.search(account_class.__name__)
if match is not None:
account_type = match.group(1)
list_func(disco_items, account_class,
from_jid.bare(), account_type)
else:
self.__logger.error(account_class.__name__ +
" name not well formed")
disco_items = self._list_accounts(self.account_classes[0],
from_jid.bare())
return disco_items
###### get_register handlers ######
@@ -1014,7 +1051,7 @@ class AccountManager(object):
return result
def _list_accounts(self, disco_items, _account_class, bare_from_jid,
def _list_accounts(self, _account_class, bare_from_jid,
account_type=""):
"""List accounts in disco_items for given _account_class and user jid"""
if account_type is not None and account_type != "":
@@ -1023,14 +1060,19 @@ class AccountManager(object):
else:
resource = ""
self.db_connect()
for _account in _account_class.select(_account_class.q.user_jid == \
unicode(bare_from_jid)):
accounts = _account_class.select(_account_class.q.user_jid == \
unicode(bare_from_jid))
if accounts.count() == 0:
return None
disco_items = DiscoItems()
for _account in accounts:
self.__logger.debug(str(_account))
DiscoItem(disco_items,
JID(unicode(_account.jid) + resource),
account_type + _account.name,
_account.long_name)
self.db_disconnect()
return disco_items
def _get_account_class(self, account_class_name):
"""Return account class definition from declared classes in
@@ -1150,7 +1192,7 @@ class AccountManager(object):
status=status,
show=show,
stanza_type=presence_type)]
def _send_presence_available(self, _account, show, lang_class):
"""Send available presence to account's user and ask for password
if necessary"""
@@ -1221,20 +1263,6 @@ class AccountManager(object):
% (exception)))
return result
class Handler(object):
"""handling class"""
def filter(self, stanza, lang_class):
"""Filter account to be processed by the handler
return all accounts. DB connection might already be opened."""
accounts = Account.select()
return accounts
def handle(self, stanza, lang_class, accounts):
"""Apply actions to do on given accounts
Do nothing by default"""
return []
class DefaultPresenceHandler(Handler):
"""Handle presence"""
@@ -1313,4 +1341,3 @@ class PasswordMessageHandler(Handler):
to_jid=stanza.get_from(),
subject=lang_class.password_saved_for_session,
body=lang_class.password_saved_for_session)]

View File

@@ -28,7 +28,8 @@ __revision__ = "$Id: feeder.py,v 1.3 2005/09/18 20:24:07 dax Exp $"
import logging
from jcl.jabber.component import JCLComponent, Handler
from jcl.jabber import Handler
from jcl.jabber.component import JCLComponent
from jcl.lang import Lang
from jcl.model.account import Account

View File

@@ -533,6 +533,28 @@ class JCLComponent_TestCase(unittest.TestCase):
info_query)
self.assertTrue(disco_info.has_feature("jabber:iq:register"))
def test_disco_get_info_root_unknown_node(self):
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="jcl.test.com")
disco_info = self.comp.disco_get_info("unknown", info_query)
self.assertEquals(disco_info, None)
def test_disco_get_info_command_list(self):
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="jcl.test.com")
disco_info = self.comp.disco_get_info("list", info_query)
self.assertNotEquals(disco_info, None)
self.assertTrue(disco_info.has_feature("http://jabber.org/protocol/commands"))
self.assertEquals(len(disco_info.get_identities()), 1)
self.assertEquals(disco_info.get_identities()[0].get_category(),
"automation")
self.assertEquals(disco_info.get_identities()[0].get_type(),
"command-node")
self.assertEquals(disco_info.get_identities()[0].get_name(),
Lang.en.command_list)
###########################################################################
# 'disco_get_items' tests
###########################################################################
@@ -546,25 +568,32 @@ class JCLComponent_TestCase(unittest.TestCase):
info_query = Iq(stanza_type = "get", \
from_jid = "user1@test.com", \
to_jid = "jcl.test.com")
disco_items = self.comp.disco_get_items(None, info_query)
disco_items = self.comp.disco_get_items("unknown", info_query)
self.assertEquals(len(disco_items.get_items()), 1)
disco_item = disco_items.get_items()[0]
self.assertEquals(disco_item.get_jid(), account1.jid)
self.assertEquals(disco_item.get_node(), account1.name)
self.assertEquals(disco_item.get_name(), account1.long_name)
def test_disco_get_items_unknown_node(self):
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="jcl.test.com")
disco_items = self.comp.disco_get_items(None, info_query)
self.assertEquals(disco_items, None)
def test_disco_get_items_1type_with_node(self):
"""get_items on an account. Must return nothing"""
account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL)
account1 = Account(user_jid = "user1@test.com", \
name = "account1", \
jid = "account1@jcl.test.com")
account1 = Account(user_jid="user1@test.com",
name="account1",
jid="account1@jcl.test.com")
del account.hub.threadConnection
info_query = Iq(stanza_type = "get", \
from_jid = "user1@test.com", \
to_jid = "account1@jcl.test.com")
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="account1@jcl.test.com")
disco_items = self.comp.disco_get_items("account1", info_query)
self.assertEquals(disco_items.get_items(), [])
self.assertEquals(disco_items, None)
def test_disco_get_items_2types_no_node(self):
"""get_items on main entity. Must account types"""
@@ -663,7 +692,7 @@ class JCLComponent_TestCase(unittest.TestCase):
from_jid = "user1@test.com", \
to_jid = "account1@jcl.test.com/Example")
disco_items = self.comp.disco_get_items("Example/account1", info_query)
self.assertEquals(disco_items.get_items(), [])
self.assertEquals(disco_items, None)
def test_disco_get_items_2types_with_long_node2(self):
"""get_items on a second type account. Must return nothing"""
@@ -677,8 +706,19 @@ class JCLComponent_TestCase(unittest.TestCase):
from_jid = "user1@test.com", \
to_jid = "account1@jcl.test.com/Example2")
disco_items = self.comp.disco_get_items("Example2/account1", info_query)
self.assertEquals(disco_items.get_items(), [])
self.assertEquals(disco_items, None)
def test_disco_get_items_list_commands(self):
info_query = Iq(stanza_type="get",
from_jid="user1@test.com",
to_jid="jcl.test.com")
disco_items = self.comp.disco_get_items("http://jabber.org/protocol/commands",
info_query)
self.assertEquals(len(disco_items.get_items()), 1)
item = disco_items.get_items()[0]
self.assertEquals(item.get_node(), "list")
self.assertEquals(item.get_name(), Lang.en.command_list)
###########################################################################
# 'handle_get_version' tests
###########################################################################

View File

@@ -110,6 +110,8 @@ class Lang:
get_gateway_desc = u"Please enter the email address of your contact"
get_gateway_prompt = u"Email address"
command_list = u"List accounts"
class fr:
component_name = u"composant générique Jabber Component Library"
register_title = u"Enregistrement d'une nouvelle connexion"
@@ -151,6 +153,8 @@ class Lang:
get_gateway_desc = u"Entrer l'adresse email de votre contact"
get_gateway_prompt = u"Adresse email"
command_list = u"List les comptes"
class nl:
# TODO: when finish, delete this line and uncomment in tests/lang.py the makeSuite(Language_nl_TestCase, 'test') line
register_title = u"Registratie van verbindingen voor Jabber Mail"

View File

@@ -32,7 +32,7 @@ from sqlobject.dbconnection import ConnectionHub
from sqlobject.joins import MultipleJoin
from jcl.lang import Lang
from jcl.jabber.error import FieldError
from jcl.error import FieldError
OFFLINE = "offline"
ONLINE = "online"

View File

@@ -27,7 +27,7 @@ import os
from sqlobject import *
from sqlobject.dbconnection import TheURIOpener
from jcl.jabber.error import FieldError
from jcl.error import FieldError
from jcl.model import account
from jcl.model.account import Account, PresenceAccount

View File

@@ -114,6 +114,8 @@ class Language_TestCase(unittest.TestCase):
self.assertNotEquals(self.lang_class.get_gateway_desc, None)
self.assertNotEquals(self.lang_class.get_gateway_prompt, None)
self.assertNotEquals(self.lang_class.command_list, None)
class Language_fr_TestCase(Language_TestCase):
def setUp(self):
self.lang_class = Lang.fr