# HG changeset patch # User Paul Boddie # Date 1298827978 -3600 # Node ID a9f6c8cd83408c2d2537c76174bfa3cc438172c8 # Parent 2ed5fa92892e43caa79a90d7b0b98df3ecc05f56 Fixed the XMPP test to perform in-band registration and deregistration, adding deregistration ("unregister") support to the XMPP module. Added a proper introduction and some XMPP-related notes to the documentation. diff -r 2ed5fa92892e -r a9f6c8cd8340 README.txt --- a/README.txt Thu Jul 09 20:48:31 2009 +0200 +++ b/README.txt Sun Feb 27 18:32:58 2011 +0100 @@ -1,7 +1,10 @@ Introduction ------------ -See docs/index.html for the libxml2dom documentation. +The libxml2dom package provides a traditional DOM wrapper around the Python +bindings for libxml2 providing basic support for XML and HTML processing. +Experimental support is also provided for a number of XML technologies +including SOAP, SVG, XML-RPC and XMPP. Compatibility Warnings ---------------------- @@ -71,6 +74,20 @@ libxml2dom.macrolib implementation, too). A way is needed to get libxml2 to do the node copying itself. +Testing the XMPP support can be awkward, particularly when trying to get user +registration to work. If testing with ejabberd, it can be useful to run the +'ejabberdctl register' command in order to register a user that can then be +used with the test_xmpp.py program. Alternatively, edit the ejabberd.conf file +(found in /etc/ejabberd on Debian, for example) changing... + + {access, register, [{deny, all}]}. + +...to... + + {access, register, [{allow, all}]}. + +...to permit in-band registration. + New in libxml2dom 0.5 (Changes since libxml2dom 0.4.7) ------------------------------------------------------ diff -r 2ed5fa92892e -r a9f6c8cd8340 libxml2dom/xmpp.py --- a/libxml2dom/xmpp.py Thu Jul 09 20:48:31 2009 +0200 +++ b/libxml2dom/xmpp.py Sun Feb 27 18:32:58 2011 +0100 @@ -10,7 +10,7 @@ See: http://www.xmpp.org/rfcs/rfc3920.html See: http://www.xmpp.org/rfcs/rfc3921.html -Copyright (C) 2007 Paul Boddie +Copyright (C) 2007, 2009 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free @@ -202,7 +202,7 @@ # NOTE: the specifications. b64value = base64.encodestring("%s\x00%s\x00%s" % (jid, username, password)) - text = self.ownerDocument.createTextNode(b64value) + text = self.ownerDocument.createTextNode(b64value.strip()) self.appendChild(text) mechanism = property(_mechanism, _setMechanism) @@ -361,7 +361,7 @@ return (self.xpath("session:session") or [None])[0] bind = property(_bind) - query = property(_query) + registration = query = property(_query) session = property(_session) def createBind(self): @@ -386,9 +386,18 @@ self.type = "get" def makeRegistration(self): + query = self.createQuery() + self.appendChild(query) self.id = "register2" self.type = "set" + def makeUnregistration(self): + query = self.createQuery() + self.appendChild(query) + query.appendChild(self.ownerDocument.createElement("remove")) + self.id = "unreg1" + self.type = "set" + def makeSession(self, host): session = self.createSession() self.appendChild(session) @@ -499,7 +508,23 @@ if doc is None: return None else: - return doc.documentElement + stanza = doc.documentElement + + # Add implied namespace (from the outermost element). + # NOTE: This should possibly use the real namespace details from the + # NOTE: stream element. + + if stanza.namespaceURI is None: + new_stanza = self.createStanza(XMPP_CLIENT_NAMESPACE, stanza.name) + new_doc = new_stanza.ownerDocument + for attribute in stanza.attributes: + new_stanza.attributes.setNamedItemNS(attribute) + for child in stanza.childNodes: + n = new_doc.importNode(child, 1) + new_stanza.appendChild(n) + stanza = new_stanza + + return stanza # Stanza creation. diff -r 2ed5fa92892e -r a9f6c8cd8340 tests/xmpp_test.py --- a/tests/xmpp_test.py Thu Jul 09 20:48:31 2009 +0200 +++ b/tests/xmpp_test.py Sun Feb 27 18:32:58 2011 +0100 @@ -1,107 +1,195 @@ #!/usr/bin/env python +""" +A test of XMPP support, providing a simple chat interface. +""" + import libxml2dom.xmpp +import os import sys -sender = "sender" in sys.argv -receiver = "receiver" in sys.argv -if not (sender or receiver): - print "Please specify sender or receiver." +try: + username, domain, password, mode = sys.argv[1:5] +except ValueError: + print sys.argv[0], " [ ]" + print + print "For example:" + print + print sys.argv[0], "libxml2dom localhost jabber listen # to listen" + print sys.argv[0], "libxml2dom localhost jabber contact 1234 # to contact a listener" sys.exit(1) -if len(sys.argv) > 2: - peer = sys.argv[2] -elif sender: - peer = "paulb@localhost/receiver" +receiving = mode == "listen" +register = mode == "register" +unregister = mode == "unregister" + +# Get a peer to send messages to. + +if not receiving and not register and not unregister: + if len(sys.argv) > 5: + peer = "%s@%s/%s" % (username, domain, sys.argv[5]) + else: + print "Need to specify a peer to send messages to." + sys.exit(1) + +# Connect to the XMPP server. s = libxml2dom.xmpp.Session(("localhost", 5222)) d = s.connect("localhost") -print "---- 1 ----" -print d.toString() + +print "---- Connection ----" +print d.toString(prettyprint=1) + +# Register if requested. + +if register: + print "---- Register ----" + iq = s.createIq() + iq.makeQuery() + print iq.toString(prettyprint=1) + s.send(iq) + + print "---- Response ----" + d = s.receive() + print d.toString(prettyprint=1) -auth = s.createAuth() -auth.mechanism = "PLAIN" -auth.setCredentials("paulb@localhost", "paulb", "jabber") -s.send(auth) -print "---- 2 ----" -d = s.receive() -print "---- 3 ----" -print d.toString() + if not d.query.xpath("register:registered"): + instructions = d.query.xpath("register:instructions") + if instructions: + print instructions[0].textContent -if d.localName == "failure": - if d.reason == "not-authorized": - print "Not authorized: must register first!" + print "---- Completing form ----" + iq = s.createIq() + iq.makeRegistration() + iq.registration["username"] = username + iq.registration["password"] = password + print iq.toString(prettyprint=1) + s.send(iq) + + print "---- Response ----" + d = s.receive() + print d.toString(prettyprint=1) + s.disconnect() sys.exit(1) +print "---- Authentication ----" +auth = s.createAuth() +auth.mechanism = "PLAIN" +auth.setCredentials(username, username, password) +print auth.toString(prettyprint=1) +s.send(auth) + +print "---- Response ----" +d = s.receive() +print d.toString(prettyprint=1) + +if d.localName == "failure": + if d.reason == "not-authorized": + print "Not authorized: perhaps register first!" + s.disconnect() + sys.exit(1) + +# Reconnect. + d = s.connect("localhost") -print "---- 4 ----" -print d.toString() +print "---- Authenticated connection ----" +print d.toString(prettyprint=1) + +print "---- Binding ----" iq = s.createIq() iq.makeBind() -if sender: - iq.bind.resource = "sender" -else: - iq.bind.resource = "receiver" +iq.bind.resource = str(os.getpid()) +print iq.toString(prettyprint=1) s.send(iq) -print "---- 5 ----" + +print "---- Response ----" d = s.receive() -print "---- 6 ----" -print d.toString() +print d.toString(prettyprint=1) +print "---- Session ----" iq = s.createIq() iq.makeSession("localhost") +print iq.toString(prettyprint=1) s.send(iq) -print "---- 7 ----" + +print "---- Response ----" d = s.receive() -print "---- 8 ----" -print d.toString() +print d.toString(prettyprint=1) + +# Unregister if requested. -if sender: - message = s.createMessage() - message.from_ = "paulb@localhost/sender" - message.to = peer - message.type = "chat" - message.body = message.createBody() - text = message.ownerDocument.createTextNode("Hello!") - message.body.appendChild(text) - print "Sending..." - print message.toString() - s.send(message) +if unregister: + print "---- Unregister ----" + iq = s.createIq() + iq.makeUnregistration() + print iq.toString(prettyprint=1) + s.send(iq) + + print "---- Response ----" d = s.receive() + print d.toString(prettyprint=1) -if receiver: + s.disconnect() + sys.exit(1) + +# Otherwise, enter chat mode. + +print "---- Chatting ----" +try: while 1: - print "Listening..." - doc = s.receive() - print doc.toString() - print - print "From:", doc.from_ - print "To:", doc.to - print "Type:", doc.type - print - if doc.localName == "message": - print "Message..." - if doc.type == "chat" and doc.body: - print doc.body.textContent - elif doc.event: - print "Composing?", doc.event.composing - print "Delivered?", doc.event.delivered - print "Displayed?", doc.event.displayed - print "Offline?", doc.event.offline - print "Id:", doc.event.id - elif doc.localName == "presence": - print "Presence..." - if doc.type == "subscribe": - presence = s.createPresence() - presence.type = "subscribed" - presence.from_ = doc.to - presence.to = doc.from_ - print "Sending..." - print presence.toString() - s.send(presence) - d = s.receive() - print + if not receiving: + message = s.createMessage() + message.from_ = "%s@%s/sender" % (username, domain) + message.to = peer + message.type = "chat" + message.body = message.createBody() + print "Enter message..." + message_text = raw_input() + text = message.ownerDocument.createTextNode(message_text) + message.body.appendChild(text) + print "Sending..." + print message.toString(prettyprint=1) + s.send(message) + receiving = 1 + + if receiving: + print "Listening as %s..." % os.getpid() + d = s.receive() + print d.toString(prettyprint=1) + print + print "From:", d.from_ + print "To:", d.to + print "Type:", d.type + print + if d.localName == "message": + print "Message..." + if d.type == "chat" and d.body: + print d.body.textContent + elif d.event: + print "Composing?", d.event.composing + print "Delivered?", d.event.delivered + print "Displayed?", d.event.displayed + print "Offline?", d.event.offline + print "Id:", d.event.id + elif d.localName == "presence": + print "Presence..." + if d.type == "subscribe": + presence = s.createPresence() + presence.type = "subscribed" + presence.from_ = d.to + presence.to = d.from_ + print "Sending..." + print presence.toString(prettyprint=1) + s.send(presence) + d = s.receive() + print d.toString(prettyprint=1) + print + receiving = 0 + peer = d.from_ + +finally: + s.disconnect() # vim: tabstop=4 expandtab shiftwidth=4