Add plugable handlers and define default handlers

* pluggable handlers for presence_available, presence_unavailable, presence_subscribe, presence_subscribed, message
* define default handlers behavior DefaultSubscribeHandler, DefaultUnsubscribeHandler, DefaultPresenceHandler

darcs-hash:20070523204935-86b55-c45e0631b5694d35d8aac606c1d1f5772bc5f15a.gz
This commit is contained in:
David Rousselie
2007-05-23 22:49:35 +02:00
parent 8b9858a5bf
commit 78ab52627f
5 changed files with 307 additions and 43 deletions

View File

@@ -94,6 +94,10 @@ class JCLComponent(Component, object):
self.queue = Queue(100)
self.account_manager = AccountManager(self)
self.msg_handlers = []
self.subscribe_handlers = []
self.unsubscribe_handlers = []
self.available_handlers = []
self.unavailable_handlers = []
self.__logger = logging.getLogger("jcl.jabber.JCLComponent")
self.lang = lang
@@ -250,7 +254,19 @@ class JCLComponent(Component, object):
if send_result:
self.send_stanzas(result)
return result
def apply_registered_behavior(self, handlers, stanza, apply_all = False):
"""Execute handler if their filter method does not return None"""
result = []
self.db_connect()
for handler in handlers:
accounts = handler.filter(stanza)
if accounts is not None:
result += handler.handle(stanza, self.lang, accounts)
self.db_disconnect()
self.send_stanzas(result)
return result
def disco_get_info(self, node, info_query):
"""Discovery get info handler
"""
@@ -362,9 +378,10 @@ class JCLComponent(Component, object):
"""Handle presence availability
if presence sent to the component ('if not name'), presence is sent to
all accounts for current user. Otherwise, send presence from current account.
"""
return self.apply_behavior(stanza, \
result = self.apply_registered_behavior(self.available_handlers, stanza)
if result == []:
result = self.apply_behavior(stanza, \
lambda name, from_jid, account_type, lang_class: \
self.account_manager.account_handle_presence_available(name, \
from_jid, \
@@ -377,13 +394,16 @@ class JCLComponent(Component, object):
lang_class, \
stanza.get_show()), \
send_result = True)
return result
def handle_presence_unavailable(self, stanza):
"""Handle presence unavailability
"""
self.__logger.debug("PRESENCE_UNAVAILABLE")
return self.apply_behavior(stanza, \
result = self.apply_registered_behavior(self.unavailable_handlers, stanza)
if result == []:
result = self.apply_behavior(stanza, \
lambda name, from_jid, account_type, lang_class: \
self.account_manager.account_handle_presence_unavailable(name, \
from_jid), \
@@ -392,12 +412,15 @@ class JCLComponent(Component, object):
lambda name, from_jid, account_type, lang_class: \
self.account_manager.root_handle_presence_unavailable(from_jid), \
send_result = True)
return result
def handle_presence_subscribe(self, stanza):
"""Handle subscribe presence from user
"""
self.__logger.debug("PRESENCE_SUBSCRIBE")
return self.apply_behavior(stanza, \
result = self.apply_registered_behavior(self.subscribe_handlers, stanza)
if result == []:
result = self.apply_behavior(stanza, \
lambda name, from_jid, account_type, lang_class: \
self.account_manager.account_handle_presence_subscribe(name, \
from_jid, \
@@ -408,6 +431,7 @@ class JCLComponent(Component, object):
self.account_manager.root_handle_presence_subscribe(from_jid, \
stanza), \
send_result = True)
return result
def handle_presence_subscribed(self, stanza):
"""Handle subscribed presence from user
@@ -419,7 +443,9 @@ class JCLComponent(Component, object):
"""Handle unsubscribe presence from user
"""
self.__logger.debug("PRESENCE_UNSUBSCRIBE")
return self.apply_behavior(stanza, \
result = self.apply_registered_behavior(self.unsubscribe_handlers, stanza)
if result == []:
result = self.apply_behavior(stanza, \
lambda name, from_jid, account_type, lang_class: \
self.account_manager.account_handle_presence_unsubscribe(name, \
from_jid), \
@@ -428,6 +454,7 @@ class JCLComponent(Component, object):
lambda name, from_jid, account_type, lang_class: \
[], \
send_result = True)
return result
def handle_presence_unsubscribed(self, stanza):
"""Handle unsubscribed presence from user
@@ -444,14 +471,7 @@ class JCLComponent(Component, object):
Handle password response message
"""
self.__logger.debug("MESSAGE: " + message.get_body())
result = []
self.db_connect()
for msg_handler in self.msg_handlers:
accounts = msg_handler.filter(message)
if accounts is not None:
result += msg_handler.handle(message, self.lang, accounts)
self.db_disconnect()
self.send_stanzas(result)
self.apply_registered_behavior(self.msg_handlers, message)
return 1
###########################################################################
@@ -1093,32 +1113,71 @@ class AccountManager(object):
% (exception)))
return result
class MessageHandler(object):
"""Message handling class"""
class Handler(object):
"""handling class"""
def filter(self, message):
def filter(self, stanza):
"""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, message, lang, accounts):
def handle(self, stanza, lang, accounts):
"""Apply actions to do on given accounts
Do nothing by default"""
return []
class PasswordMessageHandler(MessageHandler):
class DefaultPresenceHandler(Handler):
"""Handle presence"""
def handle(self, presence, lang, accounts):
"""Return same presence as receive one"""
to_jid = presence.get_to()
from_jid = presence.get_from()
presence.set_to(from_jid)
presence.set_from(to_jid)
return [presence]
class DefaultSubscribeHandler(Handler):
"""Return default response to subscribe queries"""
def handle(self, stanza, lang, accounts):
"""Create subscribe response"""
result = []
result.append(Presence(from_jid = stanza.get_to(), \
to_jid = stanza.get_from(), \
stanza_type = "subscribe"))
result.append(Presence(from_jid = stanza.get_to(), \
to_jid = stanza.get_from(), \
stanza_type = "subscribed"))
return result
class DefaultUnsubscribeHandler(Handler):
"""Return default response to subscribe queries"""
def handle(self, stanza, lang, accounts):
"""Create subscribe response"""
result = []
result.append(Presence(from_jid = stanza.get_to(), \
to_jid = stanza.get_from(), \
stanza_type = "unsubscribe"))
result.append(Presence(from_jid = stanza.get_to(), \
to_jid = stanza.get_from(), \
stanza_type = "unsubscribed"))
return result
class PasswordMessageHandler(Handler):
"""Handle password message"""
def __init__(self):
"""Ḧandler constructor"""
self.password_regexp = re.compile("\[PASSWORD\]")
def filter(self, message):
def filter(self, stanza):
"""Return the uniq account associated with a name and user JID.
DB connection might already be opened."""
name = message.get_to().node
bare_from_jid = unicode(message.get_from().bare())
name = stanza.get_to().node
bare_from_jid = unicode(stanza.get_from().bare())
accounts = Account.select(\
AND(Account.q.name == name, \
Account.q.user_jid == bare_from_jid))
@@ -1128,20 +1187,20 @@ class PasswordMessageHandler(MessageHandler):
if hasattr(_account, 'password') \
and hasattr(_account, 'waiting_password_reply') \
and (getattr(_account, 'waiting_password_reply') == True) \
and self.password_regexp.search(message.get_subject()) \
and self.password_regexp.search(stanza.get_subject()) \
is not None:
return accounts
else:
return None
def handle(self, message, lang, accounts):
"""Receive password for given account"""
def handle(self, stanza, lang, accounts):
"""Receive password in stanza (must be a Message) for given account"""
_account = accounts[0]
lang_class = lang.get_lang_class_from_node(message.get_node())
_account.password = message.get_body()
lang_class = lang.get_lang_class_from_node(stanza.get_node())
_account.password = stanza.get_body()
_account.waiting_password_reply = False
return [Message(from_jid = _account.jid, \
to_jid = message.get_from(), \
to_jid = stanza.get_from(), \
stanza_type = "normal", \
subject = lang_class.password_saved_for_session, \
body = lang_class.password_saved_for_session)]

View File

@@ -28,6 +28,7 @@ import threading
import time
import sys
import os
import re
from sqlobject import *
from sqlobject.dbconnection import TheURIOpener
@@ -40,7 +41,7 @@ from pyxmpp.presence import Presence
from pyxmpp.message import Message
from pyxmpp.jabber.dataforms import Form, Field, Option
from jcl.jabber.component import JCLComponent, MessageHandler, PasswordMessageHandler
from jcl.jabber.component import JCLComponent, Handler, PasswordMessageHandler, DefaultSubscribeHandler, DefaultUnsubscribeHandler, DefaultPresenceHandler
from jcl.model import account
from jcl.model.account import Account
from jcl.lang import Lang
@@ -48,9 +49,9 @@ from jcl.lang import Lang
from jcl.model.tests.account import ExampleAccount, Example2Account
if sys.platform == "win32":
DB_PATH = "/c|/temp/test.db"
DB_PATH = "/c|/temp/jcl_test.db"
else:
DB_PATH = "/tmp/test.db"
DB_PATH = "/tmp/jcl_test.db"
DB_URL = DB_PATH# + "?debug=1&debugThreading=1"
class MockStream(object):
@@ -125,6 +126,22 @@ class LangExample(Lang):
class en(Lang.en):
type_example_name = "Type Example"
class TestSubscribeHandler(DefaultSubscribeHandler):
def filter(self, message):
if re.compile(".*%.*").match(message.get_to().node):
# return no account because self.handle does not need an account
return []
else:
return None
class TestUnsubscribeHandler(DefaultUnsubscribeHandler):
def filter(self, message):
if re.compile(".*%.*").match(message.get_to().node):
# return no account because self.handle does not need an account
return []
else:
return None
class JCLComponent_TestCase(unittest.TestCase):
###########################################################################
# Utility methods
@@ -1398,6 +1415,31 @@ class JCLComponent_TestCase(unittest.TestCase):
self.assertEqual(presence_sent[0].get_from(), "account11@jcl.test.com")
self.assertTrue(isinstance(presence_sent[0], Presence))
def test_handle_presence_available_to_registered_handlers(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
self.comp.available_handlers += [DefaultPresenceHandler()]
account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL)
account11 = Account(user_jid = "user1@test.com", \
name = "account11", \
jid = "account11@jcl.test.com")
account12 = Account(user_jid = "user1@test.com", \
name = "account12", \
jid = "account12@jcl.test.com")
account2 = Account(user_jid = "user2@test.com", \
name = "account2", \
jid = "account2@jcl.test.com")
del account.hub.threadConnection
self.comp.handle_presence_available(Presence(\
stanza_type = "available", \
from_jid = "user1@test.com",\
to_jid = "user1%test.com@jcl.test.com"))
presence_sent = self.comp.stream.sent
self.assertEqual(len(presence_sent), 1)
self.assertEqual(presence_sent[0].get_to(), "user1@test.com")
self.assertEqual(presence_sent[0].get_from(), "user1%test.com@jcl.test.com")
self.assertTrue(isinstance(presence_sent[0], Presence))
def test_handle_presence_available_to_account_unknown_user(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
@@ -1606,6 +1648,33 @@ class JCLComponent_TestCase(unittest.TestCase):
presence_sent[0].xpath_eval("@type")[0].get_content(), \
"unavailable")
def test_handle_presence_unavailable_to_registered_handlers(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
self.comp.unavailable_handlers += [DefaultPresenceHandler()]
account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL)
account11 = Account(user_jid = "user1@test.com", \
name = "account11", \
jid = "account11@jcl.test.com")
account12 = Account(user_jid = "user1@test.com", \
name = "account12", \
jid = "account12@jcl.test.com")
account2 = Account(user_jid = "user2@test.com", \
name = "account2", \
jid = "account2@jcl.test.com")
del account.hub.threadConnection
self.comp.handle_presence_unavailable(Presence(\
stanza_type = "unavailable", \
from_jid = "user1@test.com",\
to_jid = "user1%test.com@jcl.test.com"))
presence_sent = self.comp.stream.sent
self.assertEqual(len(presence_sent), 1)
self.assertEqual(presence_sent[0].get_to(), "user1@test.com")
self.assertEqual(presence_sent[0].get_from(), "user1%test.com@jcl.test.com")
self.assertEqual(\
presence_sent[0].xpath_eval("@type")[0].get_content(), \
"unavailable")
def test_handle_presence_unavailable_to_account_unknown_user(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
@@ -1763,6 +1832,32 @@ class JCLComponent_TestCase(unittest.TestCase):
presence_sent = self.comp.stream.sent
self.assertEqual(len(presence_sent), 0)
def test_handle_presence_subscribe_to_registered_handlers(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
self.comp.subscribe_handlers += [DefaultSubscribeHandler()]
account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL)
account11 = Account(user_jid = "user1@test.com", \
name = "account11", \
jid = "account11@jcl.test.com")
account12 = Account(user_jid = "user1@test.com", \
name = "account12", \
jid = "account12@jcl.test.com")
account2 = Account(user_jid = "user2@test.com", \
name = "account2", \
jid = "account2@jcl.test.com")
del account.hub.threadConnection
result = self.comp.handle_presence_subscribe(Presence(\
stanza_type = "subscribe", \
from_jid = "user1@test.com",\
to_jid = "user1%test.com@jcl.test.com"))
sent = self.comp.stream.sent
self.assertEqual(len(sent), 2)
self.assertTrue(type(sent[0]), Presence)
self.assertEquals(sent[0].get_type(), "subscribe")
self.assertTrue(type(sent[1]), Presence)
self.assertEquals(sent[1].get_type(), "subscribed")
def test_handle_presence_subscribed(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
@@ -1811,6 +1906,38 @@ class JCLComponent_TestCase(unittest.TestCase):
2)
del account.hub.threadConnection
def test_handle_presence_unsubscribe_to_registered_handlers(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
self.comp.unsubscribe_handlers += [DefaultUnsubscribeHandler()]
account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL)
account11 = Account(user_jid = "user1@test.com", \
name = "account11", \
jid = "account11@jcl.test.com")
account12 = Account(user_jid = "user1@test.com", \
name = "account12", \
jid = "account12@jcl.test.com")
account2 = Account(user_jid = "user2@test.com", \
name = "account2", \
jid = "account2@jcl.test.com")
del account.hub.threadConnection
self.comp.handle_presence_unsubscribe(Presence(\
stanza_type = "unsubscribe", \
from_jid = "user1@test.com",\
to_jid = "user1%test.com@jcl.test.com"))
presence_sent = self.comp.stream.sent
self.assertEqual(len(presence_sent), 2)
presence = presence_sent[0]
self.assertEqual(presence.get_from(), "user1%test.com@jcl.test.com")
self.assertEqual(presence.get_to(), "user1@test.com")
self.assertEqual(presence.xpath_eval("@type")[0].get_content(), \
"unsubscribe")
presence = presence_sent[1]
self.assertEqual(presence.get_from(), "user1%test.com@jcl.test.com")
self.assertEqual(presence.get_to(), "user1@test.com")
self.assertEqual(presence.xpath_eval("@type")[0].get_content(), \
"unsubscribed")
def test_handle_presence_unsubscribe_to_account_unknown_user(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
@@ -1836,7 +1963,6 @@ class JCLComponent_TestCase(unittest.TestCase):
3)
del account.hub.threadConnection
def test_handle_presence_unsubscribe_to_unknown_account(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
@@ -1877,6 +2003,21 @@ class JCLComponent_TestCase(unittest.TestCase):
presence_sent[0].xpath_eval("@type")[0].get_content(), \
"unavailable")
def test_handle_presence_unsubscribed_to_registered_handler(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
self.comp.handle_presence_unsubscribed(Presence(\
stanza_type = "unsubscribed", \
from_jid = "user1@test.com",\
to_jid = "user1%test.com@jcl.test.com"))
presence_sent = self.comp.stream.sent
self.assertEqual(len(presence_sent), 1)
self.assertEqual(presence_sent[0].get_to(), "user1@test.com")
self.assertEqual(presence_sent[0].get_from(), "user1%test.com@jcl.test.com")
self.assertEqual(\
presence_sent[0].xpath_eval("@type")[0].get_content(), \
"unavailable")
def test_handle_message_password(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
@@ -1983,9 +2124,9 @@ class JCLComponent_TestCase(unittest.TestCase):
self.comp.send_stanzas(None)
self.assertEquals(len(self.comp.stream.sent), 0)
class MessageHandler_TestCase(unittest.TestCase):
class Handler_TestCase(unittest.TestCase):
def setUp(self):
self.handler = MessageHandler()
self.handler = Handler()
if os.path.exists(DB_PATH):
os.unlink(DB_PATH)
account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL)
@@ -2016,6 +2157,66 @@ class MessageHandler_TestCase(unittest.TestCase):
def test_handle(self):
self.assertEquals(self.handler.handle(None, None, None), [])
class DefaultSubscribeHandler_TestCase(unittest.TestCase):
def setUp(self):
self.handler = DefaultSubscribeHandler()
def test_handle(self):
presence = Presence(from_jid = "user1@test.com", \
to_jid = "user1%test.com@jcl.test.com", \
stanza_type = "subscribe")
result = self.handler.handle(presence, None, [])
self.assertEquals(len(result), 2)
self.assertEquals(result[0].get_to(), "user1@test.com")
self.assertEquals(result[0].get_from(), "user1%test.com@jcl.test.com")
self.assertEquals(result[0].get_type(), "subscribe")
self.assertEquals(result[1].get_to(), "user1@test.com")
self.assertEquals(result[1].get_from(), "user1%test.com@jcl.test.com")
self.assertEquals(result[1].get_type(), "subscribed")
class DefaultUnsubscribeHandler_TestCase(unittest.TestCase):
def setUp(self):
self.handler = DefaultUnsubscribeHandler()
def test_handle(self):
presence = Presence(from_jid = "user1@test.com", \
to_jid = "user1%test.com@jcl.test.com", \
stanza_type = "unsubscribe")
result = self.handler.handle(presence, None, [])
self.assertEquals(len(result), 2)
self.assertEquals(result[0].get_to(), "user1@test.com")
self.assertEquals(result[0].get_from(), "user1%test.com@jcl.test.com")
self.assertEquals(result[0].get_type(), "unsubscribe")
self.assertEquals(result[1].get_to(), "user1@test.com")
self.assertEquals(result[1].get_from(), "user1%test.com@jcl.test.com")
self.assertEquals(result[1].get_type(), "unsubscribed")
class DefaultPresenceHandler_TestCase(unittest.TestCase):
def setUp(self):
self.handler = DefaultPresenceHandler()
def test_handle_away(self):
presence = Presence(from_jid = "user1@test.com", \
to_jid = "user1%test.com@jcl.test.com", \
stanza_type = "available", \
show = "away")
result = self.handler.handle(presence, None, [])
self.assertEquals(len(result), 1)
self.assertEquals(result[0].get_to(), "user1@test.com")
self.assertEquals(result[0].get_from(), "user1%test.com@jcl.test.com")
self.assertEquals(result[0].get_type(), None)
self.assertEquals(result[0].get_show(), "away")
def test_handle_offline(self):
presence = Presence(from_jid = "user1@test.com", \
to_jid = "user1%test.com@jcl.test.com", \
stanza_type = "unavailable")
result = self.handler.handle(presence, None, [])
self.assertEquals(len(result), 1)
self.assertEquals(result[0].get_to(), "user1@test.com")
self.assertEquals(result[0].get_from(), "user1%test.com@jcl.test.com")
self.assertEquals(result[0].get_type(), "unavailable")
class PasswordMessageHandler_TestCase(unittest.TestCase):
def setUp(self):
self.handler = PasswordMessageHandler()
@@ -2129,7 +2330,10 @@ class PasswordMessageHandler_TestCase(unittest.TestCase):
def suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(JCLComponent_TestCase, 'test'))
suite.addTest(unittest.makeSuite(MessageHandler_TestCase, 'test'))
suite.addTest(unittest.makeSuite(Handler_TestCase, 'test'))
suite.addTest(unittest.makeSuite(DefaultSubscribeHandler_TestCase, 'test'))
suite.addTest(unittest.makeSuite(DefaultUnsubscribeHandler_TestCase, 'test'))
suite.addTest(unittest.makeSuite(DefaultPresenceHandler_TestCase, 'test'))
suite.addTest(unittest.makeSuite(PasswordMessageHandler_TestCase, 'test'))
return suite

View File

@@ -41,9 +41,9 @@ from jcl.model.tests.account import ExampleAccount, Example2Account
from jcl.jabber.tests.component import JCLComponent_TestCase, MockStream
if sys.platform == "win32":
DB_PATH = "/c|/temp/test.db"
DB_PATH = "/c|/temp/jcl_test.db"
else:
DB_PATH = "/tmp/test.db"
DB_PATH = "/tmp/jcl_test.db"
DB_URL = DB_PATH #+ "?debug=1&debugThreading=1"
class FeederComponent_TestCase(JCLComponent_TestCase):

View File

@@ -31,6 +31,12 @@ from jcl.jabber.error import FieldError
from jcl.model import account
from jcl.model.account import Account, PresenceAccount
if sys.platform == "win32":
DB_PATH = "/c|/temp/jcl_test.db"
else:
DB_PATH = "/tmp/jcl_test.db"
DB_URL = DB_PATH# + "?debug=1&debugThreading=1"
class ExampleAccount(Account):
login = StringCol(default = "")
password = StringCol(default = None)
@@ -110,12 +116,6 @@ class PresenceAccountExample(PresenceAccount):
lambda : 43)]
get_register_fields = classmethod(_get_register_fields)
if sys.platform == "win32":
DB_PATH = "/c|/temp/test.db"
else:
DB_PATH = "/tmp/test.db"
DB_URL = DB_PATH# + "?debug=1&debugThreading=1"
class AccountModule_TestCase(unittest.TestCase):
def test_default_post_func(self):
result = account.default_post_func("test", None)

View File

@@ -143,6 +143,7 @@ class JCLRunner(object):
commandline_args = self.__configure_commandline_args(shortopts, longopts, cleanopts)
if commandline_args.has_key("debug") or commandline_args.has_key("d"):
self.debug = True
self.logger.debug("Debug activated")
self.__apply_configfile(commandline_args, cleanopts)
self.__apply_commandline_args(commandline_args, cleanopts)