Implement VCard handler

darcs-hash:20080528061902-86b55-f2523528b8f88732a714134548d3f2316f229f7b.gz
This commit is contained in:
David Rousselie
2008-05-28 08:19:02 +02:00
parent bbedf036eb
commit 2fd9bc173b
13 changed files with 287 additions and 6 deletions

View File

@@ -24,3 +24,6 @@ pid_file: /var/run/jabber/jcl.pid
welcome_message: "Welcome to JCL" welcome_message: "Welcome to JCL"
admins: admin1@domain.com, admin2@domain.com admins: admin1@domain.com, admin2@domain.com
log_file: /var/log/jabber/jcl.log log_file: /var/log/jabber/jcl.log
[vcard]
url: http://people.happycoders.org/dax/projects/jcl

View File

@@ -44,6 +44,7 @@ from pyxmpp.jabberd.component import Component
from pyxmpp.message import Message from pyxmpp.message import Message
from pyxmpp.presence import Presence from pyxmpp.presence import Presence
from pyxmpp.jabber.dataforms import Form from pyxmpp.jabber.dataforms import Form
import pyxmpp.jabber.vcard as vcard
from jcl.error import FieldError from jcl.error import FieldError
from jcl.jabber.disco import AccountDiscoGetInfoHandler, \ from jcl.jabber.disco import AccountDiscoGetInfoHandler, \
@@ -61,6 +62,8 @@ from jcl.jabber.presence import AccountPresenceAvailableHandler, \
RootPresenceUnsubscribeHandler RootPresenceUnsubscribeHandler
from jcl.jabber.register import RootSetRegisterHandler, \ from jcl.jabber.register import RootSetRegisterHandler, \
AccountSetRegisterHandler, AccountTypeSetRegisterHandler AccountSetRegisterHandler, AccountTypeSetRegisterHandler
from jcl.jabber.vcard import DefaultVCardHandler
import jcl.model as model import jcl.model as model
from jcl.model import account from jcl.model import account
from jcl.model.account import Account, User from jcl.model.account import Account, User
@@ -639,6 +642,7 @@ class JCLComponent(Component, object):
self.set_register_handlers = [[RootSetRegisterHandler(self), self.set_register_handlers = [[RootSetRegisterHandler(self),
AccountSetRegisterHandler(self), AccountSetRegisterHandler(self),
AccountTypeSetRegisterHandler(self)]] AccountTypeSetRegisterHandler(self)]]
self.vcard_handlers = [[DefaultVCardHandler(self)]]
self.__logger = logging.getLogger("jcl.jabber.JCLComponent") self.__logger = logging.getLogger("jcl.jabber.JCLComponent")
self.lang = lang self.lang = lang
@@ -729,10 +733,15 @@ class JCLComponent(Component, object):
self.handle_get_gateway) self.handle_get_gateway)
self.stream.set_iq_set_handler("query", "jabber:iq:gateway", self.stream.set_iq_set_handler("query", "jabber:iq:gateway",
self.handle_set_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.stream.set_iq_set_handler("command", command.COMMAND_NS,
self.handle_command) self.handle_command)
self.stream.set_iq_get_handler("vCard", vcard.VCARD_NS,
self.handle_vcard)
self.stream.set_presence_handler("available", self.stream.set_presence_handler("available",
self.handle_presence_available) self.handle_presence_available)
@@ -851,6 +860,13 @@ class JCLComponent(Component, object):
self.send_stanzas(result) self.send_stanzas(result)
return 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): def handle_get_gateway(self, info_query):
"""Handle IQ-get "jabber:iq:gateway" requests. """Handle IQ-get "jabber:iq:gateway" requests.
Return prompt and description. Return prompt and description.
@@ -1056,6 +1072,14 @@ class JCLComponent(Component, object):
exc_info=True) exc_info=True)
return 1 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 # Utils
########################################################################### ###########################################################################

View File

@@ -24,6 +24,7 @@ import logging
from pyxmpp.jid import JID from pyxmpp.jid import JID
from pyxmpp.jabber.disco import DiscoInfo, DiscoItems, DiscoItem, DiscoIdentity from pyxmpp.jabber.disco import DiscoInfo, DiscoItems, DiscoItem, DiscoIdentity
import pyxmpp.jabber.vcard as vcard
import jcl.jabber as jabber import jcl.jabber as jabber
@@ -56,6 +57,8 @@ class RootDiscoGetInfoHandler(DiscoHandler):
disco_info.add_feature("jabber:iq:version") 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#info")
disco_info.add_feature("http://jabber.org/protocol/disco#items") 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: if not self.component.account_manager.has_multiple_account_type:
disco_info.add_feature("jabber:iq:register") disco_info.add_feature("jabber:iq:register")
DiscoIdentity(disco_info, self.component.name, DiscoIdentity(disco_info, self.component.name,
@@ -75,6 +78,8 @@ class AccountDiscoGetInfoHandler(DiscoHandler):
"""Implement discovery get_info on an account node""" """Implement discovery get_info on an account node"""
self.__logger.debug("account_disco_get_info") self.__logger.debug("account_disco_get_info")
disco_info = DiscoInfo(node) 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") disco_info.add_feature("jabber:iq:register")
return [disco_info] return [disco_info]

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
## ##
## message.py ## message.py
## Login : David Rousselie <dax@happycoders.org> ## Login : David Rousselie <dax@happycoders.org>

View File

@@ -6,7 +6,7 @@ import unittest
import jcl.jabber as jabber import jcl.jabber as jabber
from jcl.jabber.tests import component, feeder, command, message, presence, \ from jcl.jabber.tests import component, feeder, command, message, presence, \
disco disco, vcard
class HandlerType1: class HandlerType1:
pass pass
@@ -38,6 +38,7 @@ def suite():
test_suite.addTest(message.suite()) test_suite.addTest(message.suite())
test_suite.addTest(presence.suite()) test_suite.addTest(presence.suite())
test_suite.addTest(disco.suite()) test_suite.addTest(disco.suite())
test_suite.addTest(vcard.suite())
return test_suite return test_suite
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -3217,7 +3217,7 @@ class JCLCommandManagerShutdownCommand_TestCase(JCLCommandManagerTestCase):
self.assertTrue(self.comp.running) self.assertTrue(self.comp.running)
threads = threading.enumerate() threads = threading.enumerate()
self.assertEquals(len(threads), 2) self.assertEquals(len(threads), 2)
threading.Event().wait(1) threading.Event().wait(2)
threads = threading.enumerate() threads = threading.enumerate()
self.assertEquals(len(threads), 1) self.assertEquals(len(threads), 1)
self.assertFalse(self.comp.restart) self.assertFalse(self.comp.restart)

View File

@@ -35,6 +35,7 @@ from pyxmpp.iq import Iq
from pyxmpp.presence import Presence from pyxmpp.presence import Presence
from pyxmpp.message import Message from pyxmpp.message import Message
from pyxmpp.jabber.dataforms import Form from pyxmpp.jabber.dataforms import Form
import pyxmpp.jabber.vcard as vcard
import jcl.tests import jcl.tests
from jcl.jabber import Handler from jcl.jabber import Handler
@@ -67,11 +68,13 @@ class MockStream(object):
self.sent.append(iq) self.sent.append(iq)
def set_iq_set_handler(self, iq_type, ns, handler): 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) raise Exception("IQ type unknown: " + iq_type)
if not ns in ["jabber:iq:version", if not ns in ["jabber:iq:version",
"jabber:iq:register", "jabber:iq:register",
"jabber:iq:gateway", "jabber:iq:gateway",
"jabber:iq:last",
vcard.VCARD_NS,
"http://jabber.org/protocol/disco#items", "http://jabber.org/protocol/disco#items",
"http://jabber.org/protocol/disco#info", "http://jabber.org/protocol/disco#info",
"http://jabber.org/protocol/commands"]: "http://jabber.org/protocol/commands"]:
@@ -503,6 +506,8 @@ class JCLComponent_TestCase(JCLTestCase):
self.assertEquals(len(self.comp.stream.sent), 0) self.assertEquals(len(self.comp.stream.sent), 0)
self.assertEquals(disco_info.get_identities()[0].get_name(), self.comp.name) 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("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")) self.assertTrue(disco_info.has_feature("jabber:iq:register"))
def test_disco_get_info_multiple_account_type(self): 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.assertEquals(disco_info.get_identities()[0].get_name(),
self.comp.name) self.comp.name)
self.assertTrue(disco_info.has_feature("jabber:iq:version")) 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")) self.assertFalse(disco_info.has_feature("jabber:iq:register"))
def test_disco_get_info_node(self): 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) disco_info = self.comp.disco_get_info("node_test", info_query)
self.assertEquals(disco_info.get_node(), "node_test") self.assertEquals(disco_info.get_node(), "node_test")
self.assertEquals(len(self.comp.stream.sent), 0) 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")) self.assertTrue(disco_info.has_feature("jabber:iq:register"))
def test_disco_get_info_long_node(self): def test_disco_get_info_long_node(self):
@@ -542,6 +551,8 @@ class JCLComponent_TestCase(JCLTestCase):
info_query) info_query)
self.assertEquals(disco_info.get_node(), "node_type/node_test") self.assertEquals(disco_info.get_node(), "node_type/node_test")
self.assertEquals(len(self.comp.stream.sent), 0) 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")) self.assertTrue(disco_info.has_feature("jabber:iq:register"))
def test_disco_get_info_root_unknown_node(self): def test_disco_get_info_root_unknown_node(self):

View 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')

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
## ##
## message.py ## message.py
## Login : David Rousselie <dax@happycoders.org> ## Login : David Rousselie <dax@happycoders.org>

View 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
View 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]

View File

@@ -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" __logger.info("Testing xml node equality:\n--\n" + str(xml_ref) + "\n--\n"
+ str(xml_test) + "\n--\n") + str(xml_test) + "\n--\n")
if (xml_ref is None) ^ (xml_test is None): 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 (" __logger.error("xml_test (" + str(xml_test) + ") or xml_ref ("
+ str(xml_ref) + ") is None") + str(xml_ref) + ") is None")
return False 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: else:
return True return True
if (xml_ref is None) and (xml_test is None): if (xml_ref is None) and (xml_test is None):
return True return True
if isinstance(xml_ref, types.StringType) \ if isinstance(xml_ref, types.StringType) \
or isinstance(xml_ref, types.UnicodeType): or isinstance(xml_ref, types.UnicodeType):
libxml2.pedanticParserDefault(True)
xml_ref = libxml2.parseDoc(xml_ref).children xml_ref = libxml2.parseDoc(xml_ref).children
if isinstance(xml_test, types.StringType) \ if isinstance(xml_test, types.StringType) \
or isinstance(xml_test, types.UnicodeType): or isinstance(xml_test, types.UnicodeType):
libxml2.pedanticParserDefault(True)
xml_test = libxml2.parseDoc(xml_test).children xml_test = libxml2.parseDoc(xml_test).children
def check_equality(test_func, ref, test, strict): def check_equality(test_func, ref, test, strict):

View File

@@ -19,3 +19,5 @@ db_url: %(type)s://%(host)s%(name)s
pid_file: /var/run/jabber/test_jcl.pid pid_file: /var/run/jabber/test_jcl.pid
log_file: /tmp/jcl.log log_file: /tmp/jcl.log
[vcard]
url: http://people.happycoders.org/dax/projects/jcl