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

@@ -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
###########################################################################

View File

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

View File

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

View File

@@ -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__':

View File

@@ -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)

View File

@@ -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):

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
## 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"
+ 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

View File

@@ -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