set_register partial implementation

darcs-hash:20061028120036-86b55-08e755d4ae8f6ed4e2b895a33adfc34d2fd00257.gz
This commit is contained in:
David Rousselie
2006-10-28 14:00:36 +02:00
parent 3bbb9f999b
commit cd39609504
6 changed files with 185 additions and 35 deletions

View File

@@ -50,14 +50,14 @@ if __name__ == '__main__':
feeder_suite = unittest.makeSuite(Feeder_TestCase, "test") feeder_suite = unittest.makeSuite(Feeder_TestCase, "test")
sender_suite = unittest.makeSuite(Sender_TestCase, "test") sender_suite = unittest.makeSuite(Sender_TestCase, "test")
jcl_suite = unittest.TestSuite() jcl_suite = unittest.TestSuite()
# jcl_suite.addTest(FeederComponent_TestCase('test_handle_get_register_exist')) jcl_suite.addTest(FeederComponent_TestCase('test_handle_set_register_new'))
# jcl_suite.addTest(FeederComponent_TestCase('test_handle_presence_available_to_account_live_password')) # jcl_suite.addTest(FeederComponent_TestCase('test_handle_presence_available_to_account_live_password'))
# jcl_suite = unittest.TestSuite((feeder_component_suite)) # jcl_suite = unittest.TestSuite((feeder_component_suite))
# jcl_suite = unittest.TestSuite((component_suite)) # jcl_suite = unittest.TestSuite((component_suite))
jcl_suite = unittest.TestSuite((component_suite, # jcl_suite = unittest.TestSuite((component_suite,
feeder_component_suite, # feeder_component_suite,
feeder_suite, # feeder_suite,
sender_suite)) # sender_suite))
test_support.run_suite(jcl_suite) test_support.run_suite(jcl_suite)

View File

@@ -4,18 +4,18 @@
## Login : David Rousselie <dax@happycoders.org> ## Login : David Rousselie <dax@happycoders.org>
## Started on Wed Aug 9 21:04:42 2006 David Rousselie ## Started on Wed Aug 9 21:04:42 2006 David Rousselie
## $Id$ ## $Id$
## ##
## Copyright (C) 2006 David Rousselie ## Copyright (C) 2006 David Rousselie
## This program is free software; you can redistribute it and/or modify ## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by ## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or ## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version. ## (at your option) any later version.
## ##
## This program is distributed in the hope that it will be useful, ## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of ## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## GNU General Public License for more details.
## ##
## You should have received a copy of the GNU General Public License ## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software ## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
@@ -56,7 +56,7 @@ VERSION = "0.1"
############################################################################### ###############################################################################
# JCL implementation # JCL implementation
############################################################################### ###############################################################################
class JCLComponent(Component): class JCLComponent(Component):
"""Implement default JCL component behavior: """Implement default JCL component behavior:
- regular interval behavior - regular interval behavior
- Jabber register process (add, delete, update accounts) - Jabber register process (add, delete, update accounts)
@@ -80,7 +80,7 @@ class JCLComponent(Component):
account_class = property(get_account_class, set_account_class) account_class = property(get_account_class, set_account_class)
def __init__(self, def __init__(self,
jid, jid,
secret, secret,
@@ -106,7 +106,7 @@ class JCLComponent(Component):
self.accounts = [] self.accounts = []
self.time_unit = 60 self.time_unit = 60
self.queue = Queue(100) self.queue = Queue(100)
self.disco_info.add_feature("jabber:iq:version") self.disco_info.add_feature("jabber:iq:version")
self.disco_info.add_feature("jabber:iq:register") self.disco_info.add_feature("jabber:iq:register")
self.__logger = logging.getLogger("jcl.jabber.JCLComponent") self.__logger = logging.getLogger("jcl.jabber.JCLComponent")
@@ -115,7 +115,7 @@ class JCLComponent(Component):
signal.signal(signal.SIGINT, self.signal_handler) signal.signal(signal.SIGINT, self.signal_handler)
signal.signal(signal.SIGTERM, self.signal_handler) signal.signal(signal.SIGTERM, self.signal_handler)
def run(self): def run(self):
"""Main loop """Main loop
Connect to Jabber server Connect to Jabber server
@@ -323,10 +323,10 @@ class JCLComponent(Component):
"""Handle user registration response """Handle user registration response
""" """
self.__logger.debug("SET_REGISTER") self.__logger.debug("SET_REGISTER")
## lang_class = \ lang_class = \
## self.__lang.get_lang_class_from_node(info_query.get_node()) self.__lang.get_lang_class_from_node(info_query.get_node())
from_jid = info_query.get_from() from_jid = info_query.get_from()
## base_from_jid = unicode(from_jid.bare()) base_from_jid = unicode(from_jid.bare())
remove = info_query.xpath_eval("r:query/r:remove", \ remove = info_query.xpath_eval("r:query/r:remove", \
{"r" : "jabber:iq:register"}) {"r" : "jabber:iq:register"})
if remove: if remove:
@@ -354,7 +354,55 @@ class JCLComponent(Component):
x_data = X() x_data = X()
x_data.from_xml(query.children) x_data.from_xml(query.children)
# TODO : get info from Xdata # TODO : get info from Xdata
# "name" must not be null
name = x_data.get_field_value("name")
if name is None:
# TODO make error
print "ERROR"
self.db_connect()
accounts = self.account_class.select(\
self.account_class.q.user_jid == base_from_jid \
and self.account_class.q.name == name)
accounts_count = accounts.count()
all_accounts = self.account_class.select(\
self.account_class.q.user_jid == base_from_jid)
all_accounts_count = all_accounts.count()
if accounts_count > 1:
# TODO make error
print "ERROR"
if accounts_count == 1:
account = list(accounts)[0]
else:
account = self.account_class(user_jid = base_from_jid, \
name = name, \
jid = name + u"@" + unicode(self.jid))
for (field, field_type, field_post_func, field_default_func) in \
self.account_class.get_register_fields():
setattr(account, x_data.get_field_value(field, \
field_post_func, \
field_default_func))
info_query = info_query.make_result_response()
self.stream.send(info_query)
if all_accounts_count == 0:
self.stream.send(Presence(from_jid = self.jid, to_jid = base_from_jid, \
stanza_type = "subscribe"))
if accounts_count == 0:
self.stream.send(Message(\
from_jid = self.jid, to_jid = from_jid, \
stanza_type = "normal", \
subject = account.get_new_message_subject(lang_class), \
body = account.get_new_message_body(lang_class)))
self.stream.send(Presence(from_jid = self.get_jid(account), \
to_jid = base_from_jid, \
stanza_type = "subscribe"))
else:
self.stream.send(Message(\
from_jid = self.jid, to_jid = from_jid, \
stanza_type = "normal", \
subject = account.get_update_message_subject(lang_class), \
body = account.get_update_message_body(lang_class)))
self.db_disconnect()
def handle_presence_available(self, stanza): def handle_presence_available(self, stanza):
"""Handle presence availability """Handle presence availability
@@ -518,7 +566,7 @@ class JCLComponent(Component):
and old_status == account.OFFLINE \ and old_status == account.OFFLINE \
and _account.password == None : and _account.password == None :
self._ask_password(_account, lang_class) self._ask_password(_account, lang_class)
def _ask_password(self, _account, lang_class): def _ask_password(self, _account, lang_class):
"""Send a Jabber message to ask for account password """Send a Jabber message to ask for account password
""" """
@@ -551,6 +599,11 @@ class JCLComponent(Component):
reg_form.instructions = lang_class.register_instructions reg_form.instructions = lang_class.register_instructions
reg_form.type = "form" reg_form.type = "form"
# "name" field is mandatory
reg_form.add_field(field_type = "text-single", \
label = lang_class.account_name, \
var = "name")
for (field, field_type) in \ for (field, field_type) in \
self.account_class.get_register_fields(): self.account_class.get_register_fields():
lang_label_attr = self.account_class.__name__.lower() \ lang_label_attr = self.account_class.__name__.lower() \
@@ -570,11 +623,13 @@ class JCLComponent(Component):
"""Return register form for an existing account (update) """Return register form for an existing account (update)
""" """
reg_form = self.get_reg_form(lang_class) reg_form = self.get_reg_form(lang_class)
reg_form.fields["name"].value = account.name
reg_form.fields["name"].type = "hidden"
for (field_name, field) in reg_form.fields.items(): for (field_name, field) in reg_form.fields.items():
if hasattr(self.account_class, field_name): if hasattr(self.account_class, field_name):
field.value = getattr(account, field_name) field.value = getattr(account, field_name)
return reg_form return reg_form
########################################################################### ###########################################################################
# Virtual methods # Virtual methods
########################################################################### ###########################################################################

View File

@@ -50,7 +50,7 @@ class Field(object):
"""Jabber Xdata form Field """Jabber Xdata form Field
""" """
def __init__(self, field_type, label, var, value): def __init__(self, field_type, label, var, value):
self.__type = field_type self.type = field_type
self.__label = label self.__label = label
self.__var = var self.__var = var
self.value = value self.value = value
@@ -71,7 +71,7 @@ class Field(object):
raise Exception, "parent field should not be None" raise Exception, "parent field should not be None"
else: else:
field = parent.newChild(None, "field", None) field = parent.newChild(None, "field", None)
field.setProp("type", self.__type) field.setProp("type", self.type)
if not self.__label is None: if not self.__label is None:
field.setProp("label", self.__label) field.setProp("label", self.__label)
if not self.__var is None: if not self.__var is None:
@@ -106,6 +106,15 @@ class X(object):
self.fields_tab.append(field) self.fields_tab.append(field)
return field return field
def get_field_value(self, field_name, \
post_func = (lambda value: value), \
defaut_func = (lambda: None)):
"""Return field value processed by post_func
or return default func processing if field does not exist"""
if self.fields.has_key(field_name):
return post_func(self.fields[field_name].value)
return default_func()
def attach_xml(self, info_query): def attach_xml(self, info_query):
"""Attach this Xdata form to iq node """Attach this Xdata form to iq node
""" """

View File

@@ -88,12 +88,10 @@ class Lang:
update_title = u"Jabber mail connection update" update_title = u"Jabber mail connection update"
update_instructions = u"Modifying connection '%s'" update_instructions = u"Modifying connection '%s'"
connection_label = u"%s connection '%s'" connection_label = u"%s connection '%s'"
update_account_message_subject = u"Updated %s connection '%s'" update_account_message_subject = u"Updated account '%s'"
update_account_message_body = u"Registered with username '%s' and " \ update_account_message_body = u"Updated account"
u"password '%s' on '%s'" new_account_message_subject = u"New account '%s' created"
new_account_message_subject = u"New %s connection '%s' created" new_account_message_body = u"New account created"
new_account_message_body = u"Registered with " \
"username '%s' and password '%s' on '%s'"
ask_password_subject = u"Password request" ask_password_subject = u"Password request"
ask_password_body = u"Reply to this message with the password " \ ask_password_body = u"Reply to this message with the password " \
"for the following account: \n" \ "for the following account: \n" \
@@ -135,13 +133,11 @@ class Lang:
update_title = u"Mise à jour du compte JMC" update_title = u"Mise à jour du compte JMC"
update_instructions = u"Modification de la connexion '%s'" update_instructions = u"Modification de la connexion '%s'"
connection_label = u"Connexion %s '%s'" connection_label = u"Connexion %s '%s'"
update_account_message_subject = u"La connexion %s '%s' a été mise " \ update_account_message_subject = u"Le compte '%s' a été mis " \
u"à jour" u"à jour"
update_account_message_body = u"Nom d'utilisateur : '%s'\nMot de " \ update_account_message_body = u"Compte mis à jour"
u"passe : '%s'\nsur : '%s'" new_account_message_subject = u"Le compte '%s' a été créé"
new_account_message_subject = u"La connexion %s '%s' a été créée" new_account_message_body = u"Compte créé"
new_account_message_body = u"Nom d'utilisateur : '%s'\nMot de passe " \
u": '%s'\nsur : '%s'"
ask_password_subject = u"Demande de mot de passe" ask_password_subject = u"Demande de mot de passe"
ask_password_body = u"Répondre à ce message avec le mot de passe " \ ask_password_body = u"Répondre à ce message avec le mot de passe " \
u"du compte suivant : \n" \ u"du compte suivant : \n" \

View File

@@ -91,6 +91,32 @@ class Account(SQLObject):
def get_register_fields(cls): def get_register_fields(cls):
return [('name', "text-single")] """Return a list of tuples for X Data Form composition
A tuple is composed of:
- field_name: might be the name of one of the class attribut
- field_type: 'text-single', 'hidden', 'text-private', 'boolean',
'list-single', ...
- field_post_func: function called to process received field
- field_default_func: function to return default value (or error if
field is mandatory)
"""
return [] # "name" field is mandatory
get_register_fields = classmethod(get_register_fields) get_register_fields = classmethod(get_register_fields)
def get_new_message_subject(self, lang_class):
"""Get localized message subject for new account"""
return lang_class.new_account_message_subject % (self.name)
def get_new_message_body(self, lang_class):
"""Return localized message body for new account"""
return lang_class.new_account_message_body
def get_update_message_subject(self, lang_class):
"""Return localized message subject for existing account"""
return lang_class.new_account_message_subject % (self.name)
def get_update_message_body(self, lang_class):
"""Return localized message body for existing account"""
return lang_class.new_account_message_body

View File

@@ -41,6 +41,7 @@ from jcl.jabber.component import JCLComponent
from jcl.model import account from jcl.model import account
from jcl.model.account import Account from jcl.model.account import Account
from jcl.lang import Lang from jcl.lang import Lang
from jcl.jabber.x import X
DB_PATH = "/tmp/test.db" DB_PATH = "/tmp/test.db"
DB_URL = DB_PATH# + "?debug=1&debugThreading=1" DB_URL = DB_PATH# + "?debug=1&debugThreading=1"
@@ -362,7 +363,7 @@ class JCLComponent_TestCase(unittest.TestCase):
self.assertEquals(len(fields), 1) self.assertEquals(len(fields), 1)
self.assertEquals(len([field self.assertEquals(len([field
for field in fields \ for field in fields \
if field.prop("type") == "text-single" \ if field.prop("type") == "hidden" \
and field.prop("var") == "name" \ and field.prop("var") == "name" \
and field.prop("label") == \ and field.prop("label") == \
Lang.en.account_name]), \ Lang.en.account_name]), \
@@ -373,9 +374,72 @@ class JCLComponent_TestCase(unittest.TestCase):
self.assertEquals(len(value), 1) self.assertEquals(len(value), 1)
self.assertEquals(value[0].content, "account1") self.assertEquals(value[0].content, "account1")
def test_handle_set_register(self): def test_handle_set_register_new(self):
self.comp.stream = MockStream()
self.comp.stream_class = MockStream
x_data = X()
x_data.xmlns = "jabber:x:data"
x_data.type = "submit"
x_data.add_field(field_type = "text-single", \
var = "name", \
value = "account1")
iq_set = Iq(stanza_type = "set", \
from_jid = "user1@test.com", \
to_jid = "jcl.test.com")
query = iq_set.new_query("jabber:iq:register")
x_data.attach_xml(query)
self.comp.handle_set_register(iq_set)
account.hub.threadConnection = connectionForURI('sqlite://' + DB_URL)
accounts = self.comp.account_class.select(\
self.comp.account_class.q.user_jid == "user1@test.com" \
and self.comp.account_class.q.name == "account1")
self.assertEquals(accounts.count(), 1)
_account = accounts[0]
self.assertEquals(_account.user_jid, "user1@test.com")
self.assertEquals(_account.name, "account1")
self.assertEquals(_account.jid, "account1@jcl.test.com")
del account.hub.threadConnection
stanza_sent = self.comp.stream.sent
print str([str(stanza.get_node()) for stanza in stanza_sent])
self.assertEquals(len(stanza_sent), 4)
iq_result = stanza_sent[0]
self.assertTrue(isinstance(iq_result, Iq))
self.assertEquals(iq_result.get_node().prop("type"), "result")
self.assertEquals(iq_result.get_from(), "jcl.test.com")
self.assertEquals(iq_result.get_to(), "user1@test.com")
presence_component = stanza_sent[1]
self.assertTrue(isinstance(presence_component, Presence))
self.assertEquals(presence_component.get_from(), "jcl.test.com")
self.assertEquals(presence_component.get_to(), "user1@test.com")
self.assertEquals(presence_component.get_node().prop("type"), \
"subscribe")
message = stanza_sent[2]
self.assertTrue(isinstance(message, Message))
self.assertEquals(message.get_from(), "jcl.test.com")
self.assertEquals(message.get_to(), "user1@test.com")
self.assertEquals(message.get_subject(), \
_account.get_new_message_subject(Lang.en))
self.assertEquals(message.get_body(), \
_account.get_new_message_body(Lang.en))
presence_account = stanza_sent[3]
self.assertTrue(isinstance(presence_account, Presence))
self.assertEquals(presence_account.get_from(), "account1@jcl.test.com")
self.assertEquals(presence_account.get_to(), "user1@test.com")
self.assertEquals(presence_account.get_node().prop("type"), \
"subscribe")
def test_handle_set_register_update(self):
pass pass
def test_handle_set_register_remove(self):
pass
def test_handle_presence_available_to_component(self): def test_handle_presence_available_to_component(self):
self.comp.stream = MockStream() self.comp.stream = MockStream()
self.comp.stream_class = MockStream self.comp.stream_class = MockStream