source: wokkel/disco.py @ 108:50e3c368f2f3

wokkel-muc-client-support-24
Last change on this file since 108:50e3c368f2f3 was 108:50e3c368f2f3, checked in by Christopher Zorn <tofu@…>, 12 years ago

add basic errors and disco stuff, still need iq and message parts re #24

File size: 4.6 KB
Line 
1# -*- test-case-name: wokkel.test.test_disco -*-
2#
3# Copyright (c) 2003-2008 Ralph Meijer
4# See LICENSE for details.
5
6"""
7XMPP Service Discovery.
8
9The XMPP service discovery protocol is documented in
10U{XEP-0030<http://www.xmpp.org/extensions/xep-0030.html>}.
11"""
12
13from twisted.internet import defer
14from twisted.words.protocols.jabber import error, jid, xmlstream
15from twisted.words.xish import domish
16
17from wokkel.iwokkel import IDisco
18from wokkel.subprotocols import IQHandlerMixin, XMPPHandler
19
20NS = 'http://jabber.org/protocol/disco'
21NS_INFO = NS + '#info'
22NS_ITEMS = NS + '#items'
23
24IQ_GET = '/iq[@type="get"]'
25DISCO_INFO = IQ_GET + '/query[@xmlns="' + NS_INFO + '"]'
26DISCO_ITEMS = IQ_GET + '/query[@xmlns="' + NS_ITEMS + '"]'
27
28class DiscoFeature(domish.Element):
29    """
30    Element representing an XMPP service discovery feature.
31    """
32
33    def __init__(self, feature):
34        domish.Element.__init__(self, (NS_INFO, 'feature'),
35                                attribs={'var': feature})
36
37
38class DiscoIdentity(domish.Element):
39    """
40    Element representing an XMPP service discovery identity.
41    """
42
43    def __init__(self, category, type, name = None):
44        domish.Element.__init__(self, (NS_INFO, 'identity'),
45                                attribs={'category': category,
46                                         'type': type})
47        if name:
48            self['name'] = name
49
50
51class DiscoItem(domish.Element):
52    """
53    Element representing an XMPP service discovery item.
54    """
55
56    def __init__(self, jid, node='', name=None):
57        domish.Element.__init__(self, (NS_ITEMS, 'item'),
58                                attribs={'jid': jid.full()})
59        if node:
60            self['node'] = node
61
62        if name:
63            self['name'] = name
64
65
66class DiscoRequest(xmlstream.IQ):
67    """
68    Disco request.
69
70    @ivar namespace: Request namespace.
71    @type namespace: C{str}
72    @ivar method: Type attribute of the IQ request. Either C{'set'} or C{'get'}
73    @type method: C{str}
74    """
75
76    def __init__(self, xs, namespace=NS, method='get'):
77        xmlstream.IQ.__init__(self, xs, method)
78        self.addElement((namespace, 'query'))
79
80
81
82class DiscoHandler(XMPPHandler, IQHandlerMixin):
83    """
84    Protocol implementation for XMPP Service Discovery.
85
86    This handler will listen to XMPP service discovery requests and
87    query the other handlers in L{parent} (see L{XMPPHandlerContainer}) for
88    their identities, features and items according to L{IDisco}.
89    """
90
91    iqHandlers = {DISCO_INFO: '_onDiscoInfo',
92                  DISCO_ITEMS: '_onDiscoItems'}
93
94    def connectionInitialized(self):
95        self.xmlstream.addObserver(DISCO_INFO, self.handleRequest)
96        self.xmlstream.addObserver(DISCO_ITEMS, self.handleRequest)
97
98    def _error(self, failure):
99        failure.trap(defer.FirstError)
100        return failure.value.subFailure
101
102    def _onDiscoInfo(self, iq):
103        requestor = jid.internJID(iq["from"])
104        target = jid.internJID(iq["to"])
105        nodeIdentifier = iq.query.getAttribute("node", '')
106
107        def toResponse(results):
108            info = []
109            for i in results:
110                info.extend(i[1])
111
112            if nodeIdentifier and not info:
113                raise error.StanzaError('item-not-found')
114            else:
115                response = domish.Element((NS_INFO, 'query'))
116
117                for item in info:
118                    response.addChild(item)
119
120            return response
121
122        dl = []
123        for handler in self.parent:
124            if IDisco.providedBy(handler):
125                dl.append(handler.getDiscoInfo(requestor, target,
126                                               nodeIdentifier))
127
128        d = defer.DeferredList(dl, fireOnOneErrback=1, consumeErrors=1)
129        d.addCallbacks(toResponse, self._error)
130        return d
131
132    def _onDiscoItems(self, iq):
133        requestor = jid.internJID(iq["from"])
134        target = jid.internJID(iq["to"])
135        nodeIdentifier = iq.query.getAttribute("node", '')
136
137        def toResponse(results):
138            items = []
139            for i in results:
140                items.extend(i[1])
141
142            response = domish.Element((NS_ITEMS, 'query'))
143
144            for item in items:
145                response.addChild(item)
146
147            return response
148
149        dl = []
150        for handler in self.parent:
151            if IDisco.providedBy(handler):
152                dl.append(handler.getDiscoItems(requestor, target,
153                                                nodeIdentifier))
154
155        d = defer.DeferredList(dl, fireOnOneErrback=1, consumeErrors=1)
156        d.addCallbacks(toResponse, self._error)
157        return d
Note: See TracBrowser for help on using the repository browser.