Finish AccountManager refactoring

darcs-hash:20070405161831-86b55-592fee80ee5682e6fa13ac7a96432e362bec186a.gz
This commit is contained in:
David Rousselie
2007-04-05 18:18:31 +02:00
parent 5fb853bdfe
commit ca0d636b33
3 changed files with 244 additions and 201 deletions

View File

@@ -58,7 +58,7 @@ if __name__ == '__main__':
jcl_suite = unittest.TestSuite() jcl_suite = unittest.TestSuite()
# jcl_suite.addTest(FeederComponent_TestCase('test_handle_tick')) # jcl_suite.addTest(FeederComponent_TestCase('test_handle_tick'))
# jcl_suite.addTest(JCLComponent_TestCase('test_handle_presence_available_to_component')) # jcl_suite.addTest(JCLComponent_TestCase('test_run_go_offline'))
# jcl_suite.addTest(PresenceAccount_TestCase('test_possibles_actions')) # jcl_suite.addTest(PresenceAccount_TestCase('test_possibles_actions'))
# jcl_suite = unittest.TestSuite((component_suite)) # jcl_suite = unittest.TestSuite((component_suite))
# jcl_suite = unittest.TestSuite((presence_account_suite)) # jcl_suite = unittest.TestSuite((presence_account_suite))

View File

@@ -130,24 +130,8 @@ class JCLComponent(Component, object):
self.running = False self.running = False
if self.stream and not self.stream.eof \ if self.stream and not self.stream.eof \
and self.stream.socket is not None: and self.stream.socket is not None:
current_user_jid = None presences = self.account_manager.send_presence_all("unavailable")
self.db_connect() self.send_stanzas(presences)
# Explicit reference to account table (clauseTables) to use
# "user_jid" column with Account subclasses
for _account in \
self.account_classes[0].select(clauseTables = ["account"], \
orderBy = "user_jid"):
if current_user_jid != _account.user_jid:
current_user_jid = _account.user_jid
self.stream.send(Presence(\
from_jid = unicode(self.jid), \
to_jid = _account.user_jid, \
stanza_type = "unavailable"))
self.stream.send(Presence(\
from_jid = self.get_jid(_account), \
to_jid = _account.user_jid, \
stanza_type = "unavailable"))
self.db_disconnect()
self.wait_event.set() self.wait_event.set()
timer_thread.join(JCLComponent.timeout) timer_thread.join(JCLComponent.timeout)
self.disconnect() self.disconnect()
@@ -406,64 +390,31 @@ class JCLComponent(Component, object):
"""Handle presence unavailability """Handle presence unavailability
""" """
self.__logger.debug("PRESENCE_UNAVAILABLE") self.__logger.debug("PRESENCE_UNAVAILABLE")
from_jid = stanza.get_from() return self.apply_behavior(stanza, \
base_from_jid = unicode(from_jid.bare()) lambda name, from_jid, account_type, lang_class: \
name = stanza.get_to().node self.account_manager.account_handle_presence_unavailable(name, \
self.db_connect() from_jid), \
if not name: lambda name, from_jid, account_type, lang_class: \
accounts = self.account_classes[0].select(\ [], \
self.account_classes[0].q.user_jid == base_from_jid) lambda name, from_jid, account_type, lang_class: \
for _account in accounts: self.account_manager.root_handle_presence_unavailable(from_jid), \
_account.status = jcl.model.account.OFFLINE send_result = True)
presence = Presence(from_jid = _account.jid, \
to_jid = from_jid, \
stanza_type = "unavailable")
self.stream.send(presence)
if accounts.count() > 0:
presence = Presence(from_jid = stanza.get_to(), \
to_jid = from_jid, \
stanza_type = "unavailable")
self.stream.send(presence)
else:
accounts = self.account_classes[0].select(\
AND(self.account_classes[0].q.name == name, \
self.account_classes[0].q.user_jid == base_from_jid))
if accounts.count() > 0:
presence = Presence(from_jid = stanza.get_to(), \
to_jid = from_jid, \
stanza_type = "unavailable")
self.stream.send(presence)
self.db_disconnect()
return 1
def handle_presence_subscribe(self, stanza): def handle_presence_subscribe(self, stanza):
"""Handle subscribe presence from user """Handle subscribe presence from user
""" """
self.__logger.debug("PRESENCE_SUBSCRIBE") self.__logger.debug("PRESENCE_SUBSCRIBE")
from_jid = stanza.get_from() return self.apply_behavior(stanza, \
base_from_jid = unicode(from_jid.bare()) lambda name, from_jid, account_type, lang_class: \
name = stanza.get_to().node self.account_manager.account_handle_presence_subscribe(name, \
accounts = None from_jid, \
self.db_connect() stanza), \
if not name: lambda name, from_jid, account_type, lang_class: \
self.__logger.debug("subscribe request on main jid") [], \
accounts = self.account_classes[0].select(\ lambda name, from_jid, account_type, lang_class: \
self.account_classes[0].q.user_jid == base_from_jid) self.account_manager.root_handle_presence_subscribe(from_jid, \
else: stanza), \
self.__logger.debug("subscribe request on '" + name + "' account") send_result = True)
accounts = self.account_classes[0].select(\
AND(self.account_classes[0].q.name == name, \
self.account_classes[0].q.user_jid == base_from_jid))
if (accounts is not None \
and accounts.count() > 0):
presence = stanza.make_accept_response()
self.stream.send(presence)
else:
self.__logger.debug("Account '" + str(name) + "' for user '" + \
str(base_from_jid) + "' was not found. " + \
"Refusing subscription")
self.db_disconnect()
return 1
def handle_presence_subscribed(self, stanza): def handle_presence_subscribed(self, stanza):
"""Handle subscribed presence from user """Handle subscribed presence from user
@@ -475,25 +426,15 @@ class JCLComponent(Component, object):
"""Handle unsubscribe presence from user """Handle unsubscribe presence from user
""" """
self.__logger.debug("PRESENCE_UNSUBSCRIBE") self.__logger.debug("PRESENCE_UNSUBSCRIBE")
name = stanza.get_to().node return self.apply_behavior(stanza, \
from_jid = stanza.get_from() lambda name, from_jid, account_type, lang_class: \
base_from_jid = unicode(from_jid.bare()) self.account_manager.account_handle_presence_unsubscribe(name, \
self.db_connect() from_jid), \
accounts = self.account_classes[0].select(\ lambda name, from_jid, account_type, lang_class: \
AND(self.account_classes[0].q.name == name, \ [], \
self.account_classes[0].q.user_jid == base_from_jid)) lambda name, from_jid, account_type, lang_class: \
for _account in accounts: [], \
presence = Presence(from_jid = _account.jid, \ send_result = True)
to_jid = from_jid, \
stanza_type = "unsubscribe")
self.stream.send(presence)
_account.destroySelf()
presence = Presence(from_jid = _account.jid, \
to_jid = from_jid, \
stanza_type = "unsubscribed")
self.stream.send(presence)
self.db_disconnect()
return 1
def handle_presence_unsubscribed(self, stanza): def handle_presence_unsubscribed(self, stanza):
"""Handle unsubscribed presence from user """Handle unsubscribed presence from user
@@ -537,79 +478,15 @@ class JCLComponent(Component, object):
########################################################################### ###########################################################################
# Utils # Utils
########################################################################### ###########################################################################
# TODO : delete
def _get_account_class(self, account_class_name):
"""Return account class definition from declared classes in
account_classes from its class name"""
self.__logger.debug("Looking for " + account_class_name)
for _account_class in self.account_classes:
if _account_class.__name__.lower() == account_class_name.lower():
self.__logger.debug(account_class_name + " found")
return _account_class
self.__logger.debug(account_class_name + " not found")
return None
# TODO : delete
def _send_presence_available(self, _account, show, lang_class):
"""Send available presence to account's user and ask for password
if necessary"""
_account.default_lang_class = lang_class
old_status = _account.status
if show is None:
_account.status = account.ONLINE
else:
_account.status = show
self.stream.send(Presence(from_jid = _account.jid, \
to_jid = _account.user_jid, \
status = _account.status_msg, \
show = show, \
stanza_type = "available"))
if hasattr(_account, 'store_password') \
and hasattr(_account, 'password') \
and _account.store_password == False \
and old_status == account.OFFLINE \
and _account.password == None :
self.ask_password(_account, lang_class)
def ask_password(self, _account, lang_class):
"""Send a Jabber message to ask for account password
"""
if hasattr(_account, 'waiting_password_reply') \
and not _account.waiting_password_reply \
and _account.status != account.OFFLINE:
_account.waiting_password_reply = True
msg = Message(from_jid = _account.jid, \
to_jid = _account.user_jid, \
stanza_type = "normal", \
subject = u"[PASSWORD] " + \
lang_class.ask_password_subject, \
body = lang_class.ask_password_body % \
(_account.name))
self.stream.send(msg)
def send_error(self, _account, exception): def send_error(self, _account, exception):
"""Send an error message only one time until _account.in_error """"""
has been reset to False""" self.send_stanzas(self.account_manager.send_error(_account, exception))
if _account.in_error == False:
_account.in_error = True
msg = Message(from_jid = _account.jid, \
to_jid = _account.user_jid, \
stanza_type = "error", \
subject = _account.default_lang_class.check_error_subject, \
body = _account.default_lang_class.check_error_body \
% (exception))
self.stream.send(msg)
type, value, stack = sys.exc_info() type, value, stack = sys.exc_info()
# TODO : not checking email here # TODO : not checking email here
self.__logger.debug("Error while first checking mail : %s\n%s" \ self.__logger.debug("Error: %s\n%s" \
% (exception, "".join(traceback.format_exception % (exception, "".join(traceback.format_exception
(type, value, stack, 5)))) (type, value, stack, 5))))
def get_jid(self, _account):
"""Return account jid based on account instance and component jid
"""
return _account.name + u"@" + unicode(self.jid)
########################################################################### ###########################################################################
# Virtual methods # Virtual methods
########################################################################### ###########################################################################
@@ -660,6 +537,7 @@ class AccountManager(object):
disco_info.add_feature("jabber:iq:version") disco_info.add_feature("jabber:iq:version")
if not self.has_multiple_account_type: if not self.has_multiple_account_type:
disco_info.add_feature("jabber:iq:register") disco_info.add_feature("jabber:iq:register")
# TODO: name is always None
DiscoIdentity(disco_info, name, \ DiscoIdentity(disco_info, name, \
category, \ category, \
type) type)
@@ -891,21 +769,22 @@ class AccountManager(object):
else: else:
return [] return []
###### presence_available handlers ######
def account_handle_presence_available(self, name, from_jid, lang_class, show): ###### presence generic handlers ######
"""Handle presence \"available\" sent to an account JID""" def account_handle_presence(self, name, from_jid, presence_func):
"""Handle presence sent to an account JID"""
result = [] result = []
self.db_connect() self.db_connect()
accounts = Account.select(\ accounts = Account.select(\
AND(Account.q.name == name, \ AND(Account.q.name == name, \
Account.q.user_jid == unicode(from_jid.bare()))) Account.q.user_jid == unicode(from_jid.bare())))
if accounts.count() > 0: if accounts.count() > 0:
result.extend(self._send_presence_available(accounts[0], show, lang_class)) result.extend(presence_func(accounts[0]))
self.db_disconnect() self.db_disconnect()
return result return result
def root_handle_presence_available(self, from_jid, lang_class, show): def root_handle_presence(self, from_jid, presence_func, root_presence_func):
"""Handle presence \"available\" sent to component JID""" """handle presence sent to component JID"""
result = [] result = []
self.db_connect() self.db_connect()
accounts = Account.select(\ accounts = Account.select(\
@@ -913,21 +792,128 @@ class AccountManager(object):
accounts_length = 0 accounts_length = 0
for _account in accounts: for _account in accounts:
accounts_length += 1 accounts_length += 1
result.extend(self._send_presence_available(_account, \ result.extend(presence_func(_account))
show, \
lang_class))
self.db_disconnect() self.db_disconnect()
if (accounts_length > 0): if (accounts_length > 0):
result.append(Presence(from_jid = self.component.jid, \ result.extend(root_presence_func(accounts_length))
to_jid = from_jid, \
status = \
str(accounts_length) \
+ lang_class.message_status, \
show = show, \
stanza_type = "available"))
return result return result
def send_presence_all(self, presence):
"""Send presence to all account. Optimized to use only one sql
request"""
current_user_jid = None
result = []
self.db_connect()
# Explicit reference to account table (clauseTables) to use
# "user_jid" column with Account subclasses
for _account in \
Account.select(clauseTables = ["account"], \
orderBy = "user_jid"):
if current_user_jid != _account.user_jid:
current_user_jid = _account.user_jid
result.extend(getattr(self, "_send_presence_" + \
presence + "_root")(_account.user_jid))
result.extend(getattr(self, "_send_presence_" + \
presence)(_account))
self.db_disconnect()
return result
###### presence_available handlers ######
def account_handle_presence_available(self, name, from_jid, lang_class, show):
"""Handle presence \"available\" sent to an account JID"""
return self.account_handle_presence(name, from_jid, \
lambda _account:\
self._send_presence_available(_account, \
show, \
lang_class))
def root_handle_presence_available(self, from_jid, lang_class, show):
"""Handle presence \"available\" sent to component JID"""
return self.root_handle_presence(from_jid, \
lambda _account:\
self._send_presence_available(_account, \
show, \
lang_class), \
lambda nb_accounts : \
self._send_presence_available_root(from_jid, \
show, \
str(nb_accounts) \
+ lang_class.message_status))
###### presence_unavailable handlers ######
def account_handle_presence_unavailable(self, name, from_jid):
"""Handle presence \"unavailable\" sent to an account JID"""
return self.account_handle_presence(name, from_jid, \
lambda _account:\
self._send_presence_unavailable(_account))
def root_handle_presence_unavailable(self, from_jid):
"""Handle presence \"unavailable\" sent to component JID"""
return self.root_handle_presence(from_jid, \
lambda _account:\
self._send_presence_unavailable(_account), \
lambda nb_accounts: \
self._send_presence_unavailable_root(from_jid))
###### presence_subscribe handlers ######
def account_handle_presence_subscribe(self, name, from_jid, stanza):
"""Handle \"subscribe\" iq sent to an account JID"""
if self._has_account(from_jid, name):
return [stanza.make_accept_response()]
else:
self.__logger.debug("Account '" + str(name) + "' for user '" + \
unicode(from_jid.bare()) + "' was not found. " + \
"Refusing subscription")
return []
def root_handle_presence_subscribe(self, from_jid, stanza):
"""Handle \"subscribe\" iq sent to component JID"""
if self._has_account(from_jid):
return [stanza.make_accept_response()]
else:
self.__logger.debug("User '" + \
unicode(from_jid.bare()) + "' doesn't have any account. " + \
"Refusing subscription")
return []
###### presence_unsubscribe handler (only apply to account) ######
def account_handle_presence_unsubscribe(self, name, from_jid):
"""Handle \"unsubscribe\" iq sent to account JID"""
result = []
self.db_connect()
accounts = Account.select(\
AND(Account.q.name == name, \
Account.q.user_jid == unicode(from_jid.bare())))
for _account in accounts:
result.append(Presence(from_jid = _account.jid, \
to_jid = from_jid, \
stanza_type = "unsubscribe"))
result.append(Presence(from_jid = _account.jid, \
to_jid = from_jid, \
stanza_type = "unsubscribed"))
_account.destroySelf()
self.db_disconnect()
return result
###### Utils methods ###### ###### Utils methods ######
def _has_account(self, from_jid, name = None):
"""Check if user with \"from_jid\" JID has an account.
Check if an account named \"name\" exist if \"name\" given."""
self.db_connect()
if name is None:
accounts = Account.select(\
Account.q.user_jid == unicode(from_jid.bare()))
else:
accounts = Account.select(\
AND(Account.q.name == name, \
Account.q.user_jid == unicode(from_jid.bare())))
result = (accounts is not None \
and accounts.count() > 0)
self.db_disconnect()
return result
def _list_accounts(self, disco_items, _account_class, bare_from_jid, account_type = ""): def _list_accounts(self, disco_items, _account_class, bare_from_jid, account_type = ""):
"""List accounts in disco_items for given _account_class and user jid""" """List accounts in disco_items for given _account_class and user jid"""
if account_type is not None and account_type != "": if account_type is not None and account_type != "":
@@ -1030,6 +1016,33 @@ class AccountManager(object):
"""Compose account jid from account name""" """Compose account jid from account name"""
return name + u"@" + unicode(self.component.jid) return name + u"@" + unicode(self.component.jid)
def _send_presence_unavailable_root(self, to_jid):
"""Send unavailable presence to account's user from root JID"""
result = []
result.append(Presence(from_jid = self.component.jid, \
to_jid = to_jid, \
stanza_type = "unavailable"))
return result
def _send_presence_unavailable(self, _account):
"""Send unavailable presence to account's user"""
result = []
_account.status = account.OFFLINE
result.append(Presence(from_jid = _account.jid, \
to_jid = _account.user_jid, \
stanza_type = "unavailable"))
return result
def _send_presence_available_root(self, to_jid, show, status_msg):
"""Send available presence to account's user from root JID"""
result = []
result.append(Presence(from_jid = self.component.jid, \
to_jid = to_jid, \
status = status_msg, \
show = show, \
stanza_type = "available"))
return result
def _send_presence_available(self, _account, show, lang_class): def _send_presence_available(self, _account, show, lang_class):
"""Send available presence to account's user and ask for password """Send available presence to account's user and ask for password
if necessary""" if necessary"""
@@ -1069,3 +1082,17 @@ class AccountManager(object):
body = lang_class.ask_password_body % \ body = lang_class.ask_password_body % \
(_account.name))) (_account.name)))
return result return result
def send_error(self, _account, exception):
"""Send an error message only one time until _account.in_error
has been reset to False"""
result = []
if _account.in_error == False:
_account.in_error = True
result.append(Message(from_jid = _account.jid, \
to_jid = _account.user_jid, \
stanza_type = "error", \
subject = _account.default_lang_class.check_error_subject, \
body = _account.default_lang_class.check_error_body \
% (exception)))
return result

View File

@@ -246,32 +246,48 @@ class JCLComponent_TestCase(unittest.TestCase):
self.assertTrue(self.comp.stream.connection_stopped) self.assertTrue(self.comp.stream.connection_stopped)
if self.comp.queue.qsize(): if self.comp.queue.qsize():
raise self.comp.queue.get(0) raise self.comp.queue.get(0)
self.assertEquals(len(self.comp.stream.sent), 5) presence_sent = self.comp.stream.sent
presence = self.comp.stream.sent[0] self.assertEqual(len(presence_sent), 5)
self.assertTrue(isinstance(presence, Presence)) self.assertEqual(len([presence \
self.assertEquals(presence.get_from(), "jcl.test.com") for presence in presence_sent \
self.assertEquals(presence.get_to(), "test1@test.com") if presence.get_to_jid() == "test1@test.com"]), \
self.assertEquals(presence.get_node().prop("type"), "unavailable") 3)
presence = self.comp.stream.sent[1] self.assertEqual(\
self.assertTrue(isinstance(presence, Presence)) len([presence \
self.assertEquals(presence.get_from(), "account11@jcl.test.com") for presence in presence_sent \
self.assertEquals(presence.get_to(), "test1@test.com") if presence.get_from_jid() == \
self.assertEquals(presence.get_node().prop("type"), "unavailable") "jcl.test.com" \
presence = self.comp.stream.sent[2] and presence.xpath_eval("@type")[0].get_content() \
self.assertTrue(isinstance(presence, Presence)) == "unavailable"]), \
self.assertEquals(presence.get_from(), "account12@jcl.test.com") 2)
self.assertEquals(presence.get_to(), "test1@test.com") self.assertEqual(\
self.assertEquals(presence.get_node().prop("type"), "unavailable") len([presence \
presence = self.comp.stream.sent[3] for presence in presence_sent \
self.assertTrue(isinstance(presence, Presence)) if presence.get_from_jid() == \
self.assertEquals(presence.get_from(), "jcl.test.com") "account11@jcl.test.com" \
self.assertEquals(presence.get_to(), "test2@test.com") and presence.xpath_eval("@type")[0].get_content() \
self.assertEquals(presence.get_node().prop("type"), "unavailable") == "unavailable"]), \
presence = self.comp.stream.sent[4] 1)
self.assertTrue(isinstance(presence, Presence)) self.assertEqual(\
self.assertEquals(presence.get_from(), "account2@jcl.test.com") len([presence \
self.assertEquals(presence.get_to(), "test2@test.com") for presence in presence_sent \
self.assertEquals(presence.get_node().prop("type"), "unavailable") if presence.get_from_jid() == \
"account12@jcl.test.com" \
and presence.xpath_eval("@type")[0].get_content() \
== "unavailable"]), \
1)
self.assertEqual(len([presence \
for presence in presence_sent \
if presence.get_to_jid() == "test2@test.com"]), \
2)
self.assertEqual(\
len([presence \
for presence in presence_sent \
if presence.get_from_jid() == \
"account2@jcl.test.com" \
and presence.xpath_eval("@type")[0].get_content() \
== "unavailable"]), \
1)
########################################################################### ###########################################################################
# 'time_handler' tests # 'time_handler' tests