Implement VCard handler
darcs-hash:20080528061902-86b55-f2523528b8f88732a714134548d3f2316f229f7b.gz
This commit is contained in:
@@ -24,3 +24,6 @@ pid_file: /var/run/jabber/jcl.pid
|
||||
welcome_message: "Welcome to JCL"
|
||||
admins: admin1@domain.com, admin2@domain.com
|
||||
log_file: /var/log/jabber/jcl.log
|
||||
|
||||
[vcard]
|
||||
url: http://people.happycoders.org/dax/projects/jcl
|
||||
|
||||
@@ -44,6 +44,7 @@ from pyxmpp.jabberd.component import Component
|
||||
from pyxmpp.message import Message
|
||||
from pyxmpp.presence import Presence
|
||||
from pyxmpp.jabber.dataforms import Form
|
||||
import pyxmpp.jabber.vcard as vcard
|
||||
|
||||
from jcl.error import FieldError
|
||||
from jcl.jabber.disco import AccountDiscoGetInfoHandler, \
|
||||
@@ -61,6 +62,8 @@ from jcl.jabber.presence import AccountPresenceAvailableHandler, \
|
||||
RootPresenceUnsubscribeHandler
|
||||
from jcl.jabber.register import RootSetRegisterHandler, \
|
||||
AccountSetRegisterHandler, AccountTypeSetRegisterHandler
|
||||
from jcl.jabber.vcard import DefaultVCardHandler
|
||||
|
||||
import jcl.model as model
|
||||
from jcl.model import account
|
||||
from jcl.model.account import Account, User
|
||||
@@ -639,6 +642,7 @@ class JCLComponent(Component, object):
|
||||
self.set_register_handlers = [[RootSetRegisterHandler(self),
|
||||
AccountSetRegisterHandler(self),
|
||||
AccountTypeSetRegisterHandler(self)]]
|
||||
self.vcard_handlers = [[DefaultVCardHandler(self)]]
|
||||
|
||||
self.__logger = logging.getLogger("jcl.jabber.JCLComponent")
|
||||
self.lang = lang
|
||||
@@ -729,10 +733,15 @@ class JCLComponent(Component, object):
|
||||
self.handle_get_gateway)
|
||||
self.stream.set_iq_set_handler("query", "jabber:iq:gateway",
|
||||
self.handle_set_gateway)
|
||||
self.stream.set_iq_get_handler("query", "jabber:iq:last",
|
||||
self.handle_get_last)
|
||||
|
||||
self.stream.set_iq_set_handler("command", command.COMMAND_NS,
|
||||
self.handle_command)
|
||||
|
||||
self.stream.set_iq_get_handler("vCard", vcard.VCARD_NS,
|
||||
self.handle_vcard)
|
||||
|
||||
self.stream.set_presence_handler("available",
|
||||
self.handle_presence_available)
|
||||
|
||||
@@ -851,6 +860,13 @@ class JCLComponent(Component, object):
|
||||
self.send_stanzas(result)
|
||||
return result
|
||||
|
||||
def handle_get_last(self, info_query):
|
||||
"""
|
||||
Handle IQ-get "jabber:iq:last" requests.
|
||||
"""
|
||||
# TODO
|
||||
return 1
|
||||
|
||||
def handle_get_gateway(self, info_query):
|
||||
"""Handle IQ-get "jabber:iq:gateway" requests.
|
||||
Return prompt and description.
|
||||
@@ -1056,6 +1072,14 @@ class JCLComponent(Component, object):
|
||||
exc_info=True)
|
||||
return 1
|
||||
|
||||
def handle_vcard(self, info_query):
|
||||
"""
|
||||
Handle VCard request
|
||||
"""
|
||||
self.__logger.debug("VCard request")
|
||||
self.apply_registered_behavior(self.vcard_handlers, info_query)
|
||||
return 1
|
||||
|
||||
###########################################################################
|
||||
# Utils
|
||||
###########################################################################
|
||||
|
||||
@@ -24,6 +24,7 @@ import logging
|
||||
|
||||
from pyxmpp.jid import JID
|
||||
from pyxmpp.jabber.disco import DiscoInfo, DiscoItems, DiscoItem, DiscoIdentity
|
||||
import pyxmpp.jabber.vcard as vcard
|
||||
|
||||
import jcl.jabber as jabber
|
||||
|
||||
@@ -56,6 +57,8 @@ class RootDiscoGetInfoHandler(DiscoHandler):
|
||||
disco_info.add_feature("jabber:iq:version")
|
||||
disco_info.add_feature("http://jabber.org/protocol/disco#info")
|
||||
disco_info.add_feature("http://jabber.org/protocol/disco#items")
|
||||
disco_info.add_feature(vcard.VCARD_NS)
|
||||
disco_info.add_feature("jabber:iq:last")
|
||||
if not self.component.account_manager.has_multiple_account_type:
|
||||
disco_info.add_feature("jabber:iq:register")
|
||||
DiscoIdentity(disco_info, self.component.name,
|
||||
@@ -75,6 +78,8 @@ class AccountDiscoGetInfoHandler(DiscoHandler):
|
||||
"""Implement discovery get_info on an account node"""
|
||||
self.__logger.debug("account_disco_get_info")
|
||||
disco_info = DiscoInfo(node)
|
||||
disco_info.add_feature(vcard.VCARD_NS)
|
||||
disco_info.add_feature("jabber:iq:last")
|
||||
disco_info.add_feature("jabber:iq:register")
|
||||
return [disco_info]
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##
|
||||
## message.py
|
||||
## Login : David Rousselie <dax@happycoders.org>
|
||||
|
||||
@@ -6,7 +6,7 @@ import unittest
|
||||
import jcl.jabber as jabber
|
||||
|
||||
from jcl.jabber.tests import component, feeder, command, message, presence, \
|
||||
disco
|
||||
disco, vcard
|
||||
|
||||
class HandlerType1:
|
||||
pass
|
||||
@@ -38,6 +38,7 @@ def suite():
|
||||
test_suite.addTest(message.suite())
|
||||
test_suite.addTest(presence.suite())
|
||||
test_suite.addTest(disco.suite())
|
||||
test_suite.addTest(vcard.suite())
|
||||
return test_suite
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -3217,7 +3217,7 @@ class JCLCommandManagerShutdownCommand_TestCase(JCLCommandManagerTestCase):
|
||||
self.assertTrue(self.comp.running)
|
||||
threads = threading.enumerate()
|
||||
self.assertEquals(len(threads), 2)
|
||||
threading.Event().wait(1)
|
||||
threading.Event().wait(2)
|
||||
threads = threading.enumerate()
|
||||
self.assertEquals(len(threads), 1)
|
||||
self.assertFalse(self.comp.restart)
|
||||
|
||||
@@ -35,6 +35,7 @@ from pyxmpp.iq import Iq
|
||||
from pyxmpp.presence import Presence
|
||||
from pyxmpp.message import Message
|
||||
from pyxmpp.jabber.dataforms import Form
|
||||
import pyxmpp.jabber.vcard as vcard
|
||||
|
||||
import jcl.tests
|
||||
from jcl.jabber import Handler
|
||||
@@ -67,11 +68,13 @@ class MockStream(object):
|
||||
self.sent.append(iq)
|
||||
|
||||
def set_iq_set_handler(self, iq_type, ns, handler):
|
||||
if not iq_type in ["query", "command"]:
|
||||
if not iq_type in ["query", "command", "vCard"]:
|
||||
raise Exception("IQ type unknown: " + iq_type)
|
||||
if not ns in ["jabber:iq:version",
|
||||
"jabber:iq:register",
|
||||
"jabber:iq:gateway",
|
||||
"jabber:iq:last",
|
||||
vcard.VCARD_NS,
|
||||
"http://jabber.org/protocol/disco#items",
|
||||
"http://jabber.org/protocol/disco#info",
|
||||
"http://jabber.org/protocol/commands"]:
|
||||
@@ -503,6 +506,8 @@ class JCLComponent_TestCase(JCLTestCase):
|
||||
self.assertEquals(len(self.comp.stream.sent), 0)
|
||||
self.assertEquals(disco_info.get_identities()[0].get_name(), self.comp.name)
|
||||
self.assertTrue(disco_info.has_feature("jabber:iq:version"))
|
||||
self.assertTrue(disco_info.has_feature(vcard.VCARD_NS))
|
||||
self.assertTrue(disco_info.has_feature("jabber:iq:last"))
|
||||
self.assertTrue(disco_info.has_feature("jabber:iq:register"))
|
||||
|
||||
def test_disco_get_info_multiple_account_type(self):
|
||||
@@ -518,6 +523,8 @@ class JCLComponent_TestCase(JCLTestCase):
|
||||
self.assertEquals(disco_info.get_identities()[0].get_name(),
|
||||
self.comp.name)
|
||||
self.assertTrue(disco_info.has_feature("jabber:iq:version"))
|
||||
self.assertTrue(disco_info.has_feature(vcard.VCARD_NS))
|
||||
self.assertTrue(disco_info.has_feature("jabber:iq:last"))
|
||||
self.assertFalse(disco_info.has_feature("jabber:iq:register"))
|
||||
|
||||
def test_disco_get_info_node(self):
|
||||
@@ -529,6 +536,8 @@ class JCLComponent_TestCase(JCLTestCase):
|
||||
disco_info = self.comp.disco_get_info("node_test", info_query)
|
||||
self.assertEquals(disco_info.get_node(), "node_test")
|
||||
self.assertEquals(len(self.comp.stream.sent), 0)
|
||||
self.assertTrue(disco_info.has_feature(vcard.VCARD_NS))
|
||||
self.assertTrue(disco_info.has_feature("jabber:iq:last"))
|
||||
self.assertTrue(disco_info.has_feature("jabber:iq:register"))
|
||||
|
||||
def test_disco_get_info_long_node(self):
|
||||
@@ -542,6 +551,8 @@ class JCLComponent_TestCase(JCLTestCase):
|
||||
info_query)
|
||||
self.assertEquals(disco_info.get_node(), "node_type/node_test")
|
||||
self.assertEquals(len(self.comp.stream.sent), 0)
|
||||
self.assertTrue(disco_info.has_feature(vcard.VCARD_NS))
|
||||
self.assertTrue(disco_info.has_feature("jabber:iq:last"))
|
||||
self.assertTrue(disco_info.has_feature("jabber:iq:register"))
|
||||
|
||||
def test_disco_get_info_root_unknown_node(self):
|
||||
|
||||
79
src/jcl/jabber/tests/disco.py
Normal file
79
src/jcl/jabber/tests/disco.py
Normal file
@@ -0,0 +1,79 @@
|
||||
##
|
||||
## disco.py
|
||||
## Login : David Rousselie <dax@happycoders.org>
|
||||
## Started on Fri Jul 6 21:40:55 2007 David Rousselie
|
||||
## $Id$
|
||||
##
|
||||
## Copyright (C) 2007 David Rousselie
|
||||
## 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
|
||||
## the Free Software Foundation; either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program; if not, write to the Free Software
|
||||
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
##
|
||||
|
||||
import unittest
|
||||
|
||||
from pyxmpp.message import Message
|
||||
|
||||
from jcl.tests import JCLTestCase
|
||||
from jcl.model.tests.account import ExampleAccount
|
||||
|
||||
from jcl.model.account import User, Account
|
||||
from jcl.jabber.component import JCLComponent
|
||||
from jcl.jabber.disco import DiscoHandler, AccountTypeDiscoGetItemsHandler
|
||||
|
||||
class DiscoHandler_TestCase(JCLTestCase):
|
||||
def setUp(self):
|
||||
JCLTestCase.setUp(self, tables=[User, Account, ExampleAccount])
|
||||
self.comp = JCLComponent("jcl.test.com",
|
||||
"password",
|
||||
"localhost",
|
||||
"5347",
|
||||
self.db_url)
|
||||
self.handler = DiscoHandler(self.comp)
|
||||
|
||||
def test_default_filter(self):
|
||||
"""Test default filter behavior"""
|
||||
self.assertFalse(self.handler.filter(None, None, None))
|
||||
|
||||
def test_default_handler(self):
|
||||
"""Test default handler: do nothing"""
|
||||
self.assertEquals(self.handler.handle(None, None, None, None, None),
|
||||
None)
|
||||
|
||||
class AccountTypeDiscoGetItemsHandler_TestCase (JCLTestCase):
|
||||
"""Test AccountTypeDiscoGetItemsHandler class"""
|
||||
def setUp(self):
|
||||
JCLTestCase.setUp(self, tables=[User, Account, ExampleAccount])
|
||||
self.comp = JCLComponent("jcl.test.com",
|
||||
"password",
|
||||
"localhost",
|
||||
"5347",
|
||||
self.db_url)
|
||||
self.comp.account_manager.account_classes = (ExampleAccount,)
|
||||
self.handler = AccountTypeDiscoGetItemsHandler(self.comp)
|
||||
|
||||
def test_handler_unknown_account_type(self):
|
||||
"""Test handler with an unknown account type"""
|
||||
self.assertEquals(self.handler.handle(Message(from_jid="from@test.com"),
|
||||
None, None, None,
|
||||
"Unknown"), [])
|
||||
|
||||
def suite():
|
||||
test_suite = unittest.TestSuite()
|
||||
test_suite.addTest(unittest.makeSuite(DiscoHandler_TestCase, 'test'))
|
||||
test_suite.addTest(unittest.makeSuite(AccountTypeDiscoGetItemsHandler_TestCase,
|
||||
'test'))
|
||||
return test_suite
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(defaultTest='suite')
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##
|
||||
## message.py
|
||||
## Login : David Rousselie <dax@happycoders.org>
|
||||
|
||||
87
src/jcl/jabber/tests/vcard.py
Normal file
87
src/jcl/jabber/tests/vcard.py
Normal file
@@ -0,0 +1,87 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##
|
||||
## vcard.py
|
||||
## Login : David Rousselie <dax@happycoders.org>
|
||||
## Started on Fri Jul 6 21:40:55 2007 David Rousselie
|
||||
## $Id$
|
||||
##
|
||||
## Copyright (C) 2007 David Rousselie
|
||||
## 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
|
||||
## the Free Software Foundation; either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program; if not, write to the Free Software
|
||||
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
##
|
||||
|
||||
import unittest
|
||||
import logging
|
||||
import sys
|
||||
from ConfigParser import ConfigParser
|
||||
|
||||
from pyxmpp.iq import Iq
|
||||
|
||||
import jcl.tests
|
||||
from jcl.lang import Lang
|
||||
from jcl.jabber.component import JCLComponent
|
||||
from jcl.model.account import Account, User
|
||||
from jcl.jabber.vcard import DefaultVCardHandler
|
||||
|
||||
from jcl.model.tests.account import ExampleAccount
|
||||
from jcl.tests import JCLTestCase
|
||||
|
||||
class DefaultVCardHandler_TestCase(JCLTestCase):
|
||||
def setUp(self):
|
||||
JCLTestCase.setUp(self, tables=[User, Account, ExampleAccount])
|
||||
self.comp = JCLComponent("jcl.test.com",
|
||||
"password",
|
||||
"localhost",
|
||||
"5347",
|
||||
self.db_url)
|
||||
self.handler = DefaultVCardHandler(self.comp)
|
||||
self.comp.config = ConfigParser()
|
||||
self.comp.config.read("src/jcl/tests/jcl.conf")
|
||||
|
||||
def test_filter(self):
|
||||
"""Test DefaultVCardHandler filter. Accept any stanza"""
|
||||
self.assertEquals(self.handler.filter(None, None), True)
|
||||
|
||||
def test_handle(self):
|
||||
"""Test default VCard returned"""
|
||||
result = self.handler.handle(Iq(from_jid="jcl.test.com",
|
||||
to_jid="user@test.com",
|
||||
stanza_type="get"),
|
||||
Lang.en, True)
|
||||
self.assertEquals(len(result), 1)
|
||||
result[0].xmlnode.setNs(None)
|
||||
self.assertTrue(jcl.tests.is_xml_equal(\
|
||||
u"<iq from='user@test.com' to='jcl.test.com' type='result'>"
|
||||
+ "<vCard xmlns='vcard-temp'>"
|
||||
+ "<URL>" + self.comp.config.get("vcard", "url") + "</URL>"
|
||||
+ "<N><FAMILY>" + self.comp.name + "</FAMILY>"
|
||||
+ "<GIVEN></GIVEN>"
|
||||
+ "<MIDDLE></MIDDLE>"
|
||||
+ "<PREFIX></PREFIX>"
|
||||
+ "<SUFFIX></SUFFIX></N>"
|
||||
+ "<FN>" + self.comp.name + "</FN>"
|
||||
+ "</vCard></iq>",
|
||||
result[0].xmlnode, True))
|
||||
|
||||
def suite():
|
||||
test_suite = unittest.TestSuite()
|
||||
test_suite.addTest(unittest.makeSuite(DefaultVCardHandler_TestCase, 'test'))
|
||||
return test_suite
|
||||
|
||||
if __name__ == '__main__':
|
||||
logger = logging.getLogger()
|
||||
logger.addHandler(logging.StreamHandler())
|
||||
if '-v' in sys.argv:
|
||||
logger.setLevel(logging.INFO)
|
||||
unittest.main(defaultTest='suite')
|
||||
56
src/jcl/jabber/vcard.py
Normal file
56
src/jcl/jabber/vcard.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##
|
||||
## vcard.py
|
||||
## Login : David Rousselie <dax@happycoders.org>
|
||||
## Started on Wed Jun 20 08:19:57 2007 David Rousselie
|
||||
## $Id$
|
||||
##
|
||||
## Copyright (C) 2007 David Rousselie
|
||||
## 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
|
||||
## the Free Software Foundation; either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program; if not, write to the Free Software
|
||||
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
##
|
||||
|
||||
from pyxmpp.jabber.vcard import VCard, VCardName, VCardString
|
||||
|
||||
from jcl.jabber import Handler
|
||||
|
||||
class DefaultVCardHandler(Handler):
|
||||
"""Default VCard Handler. Return a VCard for all requests"""
|
||||
|
||||
def filter(self, stanza, lang_class):
|
||||
"""
|
||||
Do not filter any stanza
|
||||
"""
|
||||
return True
|
||||
|
||||
def handle(self, stanza, lang_class, data):
|
||||
"""
|
||||
Return default VCard
|
||||
"""
|
||||
print "Handling VCard"
|
||||
vcard_response = stanza.make_result_response()
|
||||
vcard_rfc_tab = [u"BEGIN:VCARD",
|
||||
VCardName("N", self.component.name).rfc2426()]
|
||||
if self.component.config.has_section("vcard"):
|
||||
config = self.component.config
|
||||
vcard_rfc_tab += [VCardString(field.upper(),
|
||||
unicode(config.get("vcard",
|
||||
field))).rfc2426()
|
||||
for field in config.options("vcard")]
|
||||
vcard_rfc_tab += ["END:VCARD"]
|
||||
vcard = VCard("\n".join(vcard_rfc_tab))
|
||||
## Correct PyXMPP bug
|
||||
vcard.xml_element_name = "vCard"
|
||||
vcard.as_xml(vcard_response.xmlnode)
|
||||
return [vcard_response]
|
||||
@@ -38,19 +38,30 @@ def is_xml_equal(xml_ref, xml_test, strict=False,
|
||||
__logger.info("Testing xml node equality:\n--\n" + str(xml_ref) + "\n--\n"
|
||||
+ str(xml_test) + "\n--\n")
|
||||
if (xml_ref is None) ^ (xml_test is None):
|
||||
if strict or xml_test is None:
|
||||
if xml_test is None:
|
||||
__logger.error("xml_test (" + str(xml_test) + ") or xml_ref ("
|
||||
+ str(xml_ref) + ") is None")
|
||||
return False
|
||||
elif strict:
|
||||
# libxml2 parser set content to None for empty node but
|
||||
# pyxmpp parser set content to an empty string
|
||||
if xml_test.type == "text" and xml_test.content == "":
|
||||
return True
|
||||
else:
|
||||
__logger.error("xml_test (" + str(xml_test) + ") or xml_ref ("
|
||||
+ str(xml_ref) + ") is None")
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
if (xml_ref is None) and (xml_test is None):
|
||||
return True
|
||||
if isinstance(xml_ref, types.StringType) \
|
||||
or isinstance(xml_ref, types.UnicodeType):
|
||||
libxml2.pedanticParserDefault(True)
|
||||
xml_ref = libxml2.parseDoc(xml_ref).children
|
||||
if isinstance(xml_test, types.StringType) \
|
||||
or isinstance(xml_test, types.UnicodeType):
|
||||
libxml2.pedanticParserDefault(True)
|
||||
xml_test = libxml2.parseDoc(xml_test).children
|
||||
|
||||
def check_equality(test_func, ref, test, strict):
|
||||
@@ -100,14 +111,14 @@ def is_xml_equal(xml_ref, xml_test, strict=False,
|
||||
return False
|
||||
for attr in ref.properties:
|
||||
if ref.prop(attr.name) != test.prop(attr.name):
|
||||
__logger.error("XML node attributs are different: "
|
||||
__logger.error("XML node attributs are different: "
|
||||
+ str(attr)
|
||||
+ " != " + str(test.prop(attr.name)))
|
||||
return False
|
||||
if strict_attribut:
|
||||
for attr in test.properties:
|
||||
if ref.prop(attr.name) != test.prop(attr.name):
|
||||
__logger.error("XML node attributs are different: "
|
||||
__logger.error("XML node attributs are different: "
|
||||
+ str(attr)
|
||||
+ " != " + str(ref.prop(attr.name)))
|
||||
return False
|
||||
|
||||
@@ -19,3 +19,5 @@ db_url: %(type)s://%(host)s%(name)s
|
||||
pid_file: /var/run/jabber/test_jcl.pid
|
||||
log_file: /tmp/jcl.log
|
||||
|
||||
[vcard]
|
||||
url: http://people.happycoders.org/dax/projects/jcl
|
||||
|
||||
Reference in New Issue
Block a user