source: ralphm-patches/xmpp_client_listener.patch @ 34:e46c5701df9e

Last change on this file since 34:e46c5701df9e was 34:e46c5701df9e, checked in by Ralph Meijer <ralphm@…>, 13 years ago

Add a bunch of new patches.

File size: 6.2 KB
RevLine 
[34]1diff -r c9d1bf0b783d wokkel/client.py
2--- a/wokkel/client.py  Sun Jan 10 15:01:41 2010 +0100
3+++ b/wokkel/client.py  Fri Feb 12 09:31:19 2010 +0100
4@@ -10,14 +10,27 @@
5 that should probably eventually move there.
6 """
7 
8+import base64
9+
10 from twisted.application import service
11-from twisted.internet import reactor
12+from twisted.internet import defer, reactor
13 from twisted.names.srvconnect import SRVConnector
14-from twisted.words.protocols.jabber import client, sasl, xmlstream
15+from twisted.python import log
16+from twisted.words.protocols.jabber import client, error, sasl, xmlstream
17+from twisted.words.protocols.jabber.jid import internJID as JID
18+from twisted.words.xish import domish
19 
20 from wokkel import generic
21 from wokkel.subprotocols import StreamManager
22 
23+NS_CLIENT = 'jabber:client'
24+
25+XPATH_ALL = "/*"
26+XPATH_AUTH = "/auth[@xmlns='%s']" % sasl.NS_XMPP_SASL
27+XPATH_BIND = "/iq[@type='set']/bind[@xmlns='%s']" % client.NS_XMPP_BIND
28+XPATH_SESSION = "/iq[@type='set']/session[@xmlns='%s']" % \
29+                client.NS_XMPP_SESSION
30+
31 class CheckAuthInitializer(object):
32     """
33     Check what authentication methods are available.
34@@ -51,7 +64,7 @@
35     autentication.
36     """
37 
38-    namespace = 'jabber:client'
39+    namespace = NS_CLIENT
40 
41     def __init__(self, jid, password):
42         xmlstream.ConnectAuthenticator.__init__(self, jid.host)
43@@ -186,3 +199,154 @@
44     c = XMPPClientConnector(reactor, domain, factory)
45     c.connect()
46     return factory.deferred
47+
48+
49+
50+class XMPPClientListenAuthenticator(xmlstream.ListenAuthenticator):
51+    namespace = NS_CLIENT
52+
53+    def __init__(self, domain):
54+        self.domain = domain
55+        self.failureGrace = 3
56+        self.state = 'auth'
57+
58+
59+    def associateWithStream(self, xs):
60+        xmlstream.ListenAuthenticator.associateWithStream(self, xs)
61+        self.xmlstream.addObserver(XPATH_ALL, self.onElementFallback, -1)
62+
63+
64+    def onElementFallback(self, element):
65+        if element.handled:
66+            return
67+
68+        exc = error.StreamError('not-authorized')
69+        self.xmlstream.sendStreamError(exc)
70+
71+
72+    def streamStarted(self, rootElement):
73+        xmlstream.ListenAuthenticator.streamStarted(self, rootElement)
74+
75+        # check namespace
76+        #if self.xmlstream.namespace != self.namespace:
77+        #    self.xmlstream.namespace = self.namespace
78+        #    exc = error.StreamError('invalid-namespace')
79+        #    self.xmlstream.sendStreamError(exc)
80+        #    return
81+
82+        # TODO: check domain
83+
84+        self.xmlstream.sendHeader()
85+
86+        try:
87+            stateHandlerName = 'streamStarted' + self.state.capitalize()
88+            stateHandler = getattr(self, stateHandlerName)
89+        except AttributeError:
90+            log.msg('streamStarted handler for', self.state, 'not found')
91+        else:
92+            stateHandler()
93+
94+
95+    def toState(self, state):
96+        self.state = state
97+        if state == 'initialized':
98+            self.xmlstream.removeObserver(XPATH_ALL, self.onElementFallback)
99+            self.xmlstream.addOnetimeObserver(XPATH_SESSION, self.onSession, 1)
100+            self.xmlstream.dispatch(self.xmlstream,
101+                                    xmlstream.STREAM_AUTHD_EVENT)
102+
103+
104+    def streamStartedAuth(self):
105+        features = domish.Element((xmlstream.NS_STREAMS, 'features'))
106+        features.addElement((sasl.NS_XMPP_SASL, 'mechanisms'))
107+        features.mechanisms.addElement('mechanism', content='PLAIN')
108+        self.xmlstream.send(features)
109+        self.xmlstream.addOnetimeObserver(XPATH_AUTH, self.onAuth)
110+
111+
112+    def onAuth(self, auth):
113+        auth.handled = True
114+
115+        if auth.getAttribute('mechanism') != 'PLAIN':
116+            failure = domish.Element((sasl.NS_XMPP_SASL, 'failure'))
117+            failure.addElement('invalid-mechanism')
118+            self.xmlstream.send(failure)
119+
120+            # Close stream on too many failing authentication attempts
121+            self.failureGrace -= 1
122+            if self.failureGrace == 0:
123+                self.xmlstream.sendFooter()
124+            else:
125+                self.xmlstream.addOnetimeObserver(XPATH_AUTH, self.onAuth)
126+
127+            return
128+
129+        initialResponse = base64.b64decode(unicode(auth))
130+        authzid, authcid, passwd = initialResponse.split('\x00')
131+
132+        # TODO: check passwd
133+
134+        # authenticated
135+
136+        self.username = authcid
137+
138+        success = domish.Element((sasl.NS_XMPP_SASL, 'success'))
139+        self.xmlstream.send(success)
140+        self.xmlstream.reset()
141+
142+        self.toState('bind')
143+
144+
145+    def streamStartedBind(self):
146+        features = domish.Element((xmlstream.NS_STREAMS, 'features'))
147+        features.addElement((client.NS_XMPP_BIND, 'bind'))
148+        features.addElement((client.NS_XMPP_SESSION, 'session'))
149+        self.xmlstream.send(features)
150+        self.xmlstream.addOnetimeObserver(XPATH_BIND, self.onBind)
151+
152+
153+    def doBind(self, iq):
154+        iq.handled = True
155+        self.resource = unicode(iq.bind) or 'default'
156+
157+        # TODO: check for resource conflicts
158+
159+        newJID = JID(tuple=(self.username, self.domain, self.resource))
160+
161+        reply = domish.Element((None, 'iq'))
162+        reply['type'] = 'result'
163+        if iq.getAttribute('id'):
164+            reply['id'] = iq['id']
165+        reply.addElement((client.NS_XMPP_BIND, 'bind'))
166+        reply.bind.addElement((client.NS_XMPP_BIND, 'jid'),
167+                              content=newJID.full())
168+        self.xmlstream.send(reply)
169+        return defer.succeed(newJID)
170+
171+
172+    def onBind(self, iq):
173+        def cb(jid):
174+            self.xmlstream.otherEntity = jid
175+            self.toState('initialized')
176+
177+        def eb(failure):
178+            if not isinstance(failure, error.StanzaError):
179+                log.msg(failure)
180+                exc = error.StanzaError('internal-server-error')
181+            else:
182+                exc = failure.value
183+            self.xmlstream.send(exc.toResponse(iq))
184+
185+        d = defer.maybeDeferred(self.doBind, iq)
186+        d.addCallbacks(cb, eb)
187+
188+
189+    def onSession(self, iq):
190+        iq.handled = True
191+
192+        reply = domish.Element((None, 'iq'))
193+        reply['type'] = 'result'
194+        if iq.getAttribute('id'):
195+            reply['id'] = iq['id']
196+        reply.addElement((client.NS_XMPP_SESSION, 'session'))
197+        self.xmlstream.send(reply)
Note: See TracBrowser for help on using the repository browser.