source: wokkel/generic.py @ 35:eb020b49a77d

Last change on this file since 35:eb020b49a77d was 35:eb020b49a77d, checked in by Ralph Meijer <ralphm@…>, 14 years ago

Add XMPP router and server side component authenticator.

Author: ralphm.
Fixed #30.

File size: 4.6 KB
RevLine 
[20]1# -*- test-case-name: wokkel.test.test_generic -*-
2#
3# Copyright (c) 2003-2008 Ralph Meijer
[1]4# See LICENSE for details.
5
6"""
7Generic XMPP protocol helpers.
8"""
9
10from zope.interface import implements
11
12from twisted.internet import defer
[2]13from twisted.words.protocols.jabber import error
[20]14from twisted.words.protocols.jabber.xmlstream import toResponse
[35]15from twisted.words.xish import domish, utility
[1]16
17from wokkel import disco
18from wokkel.iwokkel import IDisco
19from wokkel.subprotocols import XMPPHandler
20
21IQ_GET = '/iq[@type="get"]'
22IQ_SET = '/iq[@type="set"]'
23
24NS_VERSION = 'jabber:iq:version'
25VERSION = IQ_GET + '/query[@xmlns="' + NS_VERSION + '"]'
26
[24]27def parseXml(string):
28    """
29    Parse serialized XML into a DOM structure.
30
31    @param string: The serialized XML to be parsed, UTF-8 encoded.
32    @type string: C{str}.
33    @return: The DOM structure, or C{None} on empty or incomplete input.
34    @rtype: L{domish.Element}
35    """
36    roots = []
37    results = []
38    elementStream = domish.elementStream()
39    elementStream.DocumentStartEvent = roots.append
40    elementStream.ElementEvent = lambda elem: roots[0].addChild(elem)
41    elementStream.DocumentEndEvent = lambda: results.append(roots[0])
42    elementStream.parse(string)
43    return results and results[0] or None
44
45
46
[30]47def stripNamespace(rootElement):
48    namespace = rootElement.uri
49
50    def strip(element):
51        if element.uri == namespace:
52            element.uri = None
53            if element.defaultUri == namespace:
54                element.defaultUri = None
55            for child in element.elements():
56                strip(child)
57
58    if namespace is not None:
59        strip(rootElement)
60
61    return rootElement
62
63
64
[1]65class FallbackHandler(XMPPHandler):
66    """
67    XMPP subprotocol handler that catches unhandled iq requests.
68
69    Unhandled iq requests are replied to with a service-unavailable stanza
70    error.
71    """
72
73    def connectionInitialized(self):
74        self.xmlstream.addObserver(IQ_SET, self.iqFallback, -1)
75        self.xmlstream.addObserver(IQ_GET, self.iqFallback, -1)
76
77    def iqFallback(self, iq):
78        if iq.handled == True:
79            return
80
81        reply = error.StanzaError('service-unavailable')
82        self.xmlstream.send(reply.toResponse(iq))
83
84
[20]85
[1]86class VersionHandler(XMPPHandler):
87    """
88    XMPP subprotocol handler for XMPP Software Version.
89
90    This protocol is described in
91    U{XEP-0092<http://www.xmpp.org/extensions/xep-0092.html>}.
92    """
93
94    implements(IDisco)
95
96    def __init__(self, name, version):
97        self.name = name
98        self.version = version
99
100    def connectionInitialized(self):
101        self.xmlstream.addObserver(VERSION, self.onVersion)
102
103    def onVersion(self, iq):
[20]104        response = toResponse(iq, "result")
[1]105
[20]106        query = response.addElement((NS_VERSION, "query"))
[1]107        name = query.addElement("name", content=self.name)
[20]108        version = query.addElement("version", content=self.version)
109        self.send(response)
[1]110
111        iq.handled = True
112
[6]113    def getDiscoInfo(self, requestor, target, node):
[11]114        info = set()
115
[1]116        if not node:
[11]117            info.add(disco.DiscoFeature(NS_VERSION))
118
119        return defer.succeed(info)
[1]120
[6]121    def getDiscoItems(self, requestor, target, node):
[1]122        return defer.succeed([])
[35]123
124
125
126class XmlPipe(object):
127    """
128    XML stream pipe.
129
130    Connects two objects that communicate stanzas through an XML stream like
131    interface. Each of the ends of the pipe (sink and source) can be used to
132    send XML stanzas to the other side, or add observers to process XML stanzas
133    that were sent from the other side.
134
135    XML pipes are usually used in place of regular XML streams that are
136    transported over TCP. This is the reason for the use of the names source
137    and sink for both ends of the pipe. The source side corresponds with the
138    entity that initiated the TCP connection, whereas the sink corresponds with
139    the entity that accepts that connection. In this object, though, the source
140    and sink are treated equally.
141
142    Unlike Jabber
143    L{XmlStream<twisted.words.protocols.jabber.xmlstream.XmlStream>}s, the sink
144    and source objects are assumed to represent an eternal connected and
145    initialized XML stream. As such, events corresponding to connection,
146    disconnection, initialization and stream errors are not dispatched or
147    processed.
148
149    @ivar source: Source XML stream.
150    @ivar sink: Sink XML stream.
151    """
152
153    def __init__(self):
154        self.source = utility.EventDispatcher()
155        self.sink = utility.EventDispatcher()
156        self.source.send = lambda obj: self.sink.dispatch(obj)
157        self.sink.send = lambda obj: self.source.dispatch(obj)
Note: See TracBrowser for help on using the repository browser.