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@…>, 11 years ago

Add a bunch of new patches.

File size: 6.2 KB
  • wokkel/client.py

    diff -r c9d1bf0b783d wokkel/client.py
    a b  
    1010that should probably eventually move there.
    1111"""
    1212
     13import base64
     14
    1315from twisted.application import service
    14 from twisted.internet import reactor
     16from twisted.internet import defer, reactor
    1517from twisted.names.srvconnect import SRVConnector
    16 from twisted.words.protocols.jabber import client, sasl, xmlstream
     18from twisted.python import log
     19from twisted.words.protocols.jabber import client, error, sasl, xmlstream
     20from twisted.words.protocols.jabber.jid import internJID as JID
     21from twisted.words.xish import domish
    1722
    1823from wokkel import generic
    1924from wokkel.subprotocols import StreamManager
    2025
     26NS_CLIENT = 'jabber:client'
     27
     28XPATH_ALL = "/*"
     29XPATH_AUTH = "/auth[@xmlns='%s']" % sasl.NS_XMPP_SASL
     30XPATH_BIND = "/iq[@type='set']/bind[@xmlns='%s']" % client.NS_XMPP_BIND
     31XPATH_SESSION = "/iq[@type='set']/session[@xmlns='%s']" % \
     32                client.NS_XMPP_SESSION
     33
    2134class CheckAuthInitializer(object):
    2235    """
    2336    Check what authentication methods are available.
     
    5164    autentication.
    5265    """
    5366
    54     namespace = 'jabber:client'
     67    namespace = NS_CLIENT
    5568
    5669    def __init__(self, jid, password):
    5770        xmlstream.ConnectAuthenticator.__init__(self, jid.host)
     
    186199    c = XMPPClientConnector(reactor, domain, factory)
    187200    c.connect()
    188201    return factory.deferred
     202
     203
     204
     205class XMPPClientListenAuthenticator(xmlstream.ListenAuthenticator):
     206    namespace = NS_CLIENT
     207
     208    def __init__(self, domain):
     209        self.domain = domain
     210        self.failureGrace = 3
     211        self.state = 'auth'
     212
     213
     214    def associateWithStream(self, xs):
     215        xmlstream.ListenAuthenticator.associateWithStream(self, xs)
     216        self.xmlstream.addObserver(XPATH_ALL, self.onElementFallback, -1)
     217
     218
     219    def onElementFallback(self, element):
     220        if element.handled:
     221            return
     222
     223        exc = error.StreamError('not-authorized')
     224        self.xmlstream.sendStreamError(exc)
     225
     226
     227    def streamStarted(self, rootElement):
     228        xmlstream.ListenAuthenticator.streamStarted(self, rootElement)
     229
     230        # check namespace
     231        #if self.xmlstream.namespace != self.namespace:
     232        #    self.xmlstream.namespace = self.namespace
     233        #    exc = error.StreamError('invalid-namespace')
     234        #    self.xmlstream.sendStreamError(exc)
     235        #    return
     236
     237        # TODO: check domain
     238
     239        self.xmlstream.sendHeader()
     240
     241        try:
     242            stateHandlerName = 'streamStarted' + self.state.capitalize()
     243            stateHandler = getattr(self, stateHandlerName)
     244        except AttributeError:
     245            log.msg('streamStarted handler for', self.state, 'not found')
     246        else:
     247            stateHandler()
     248
     249
     250    def toState(self, state):
     251        self.state = state
     252        if state == 'initialized':
     253            self.xmlstream.removeObserver(XPATH_ALL, self.onElementFallback)
     254            self.xmlstream.addOnetimeObserver(XPATH_SESSION, self.onSession, 1)
     255            self.xmlstream.dispatch(self.xmlstream,
     256                                    xmlstream.STREAM_AUTHD_EVENT)
     257
     258
     259    def streamStartedAuth(self):
     260        features = domish.Element((xmlstream.NS_STREAMS, 'features'))
     261        features.addElement((sasl.NS_XMPP_SASL, 'mechanisms'))
     262        features.mechanisms.addElement('mechanism', content='PLAIN')
     263        self.xmlstream.send(features)
     264        self.xmlstream.addOnetimeObserver(XPATH_AUTH, self.onAuth)
     265
     266
     267    def onAuth(self, auth):
     268        auth.handled = True
     269
     270        if auth.getAttribute('mechanism') != 'PLAIN':
     271            failure = domish.Element((sasl.NS_XMPP_SASL, 'failure'))
     272            failure.addElement('invalid-mechanism')
     273            self.xmlstream.send(failure)
     274
     275            # Close stream on too many failing authentication attempts
     276            self.failureGrace -= 1
     277            if self.failureGrace == 0:
     278                self.xmlstream.sendFooter()
     279            else:
     280                self.xmlstream.addOnetimeObserver(XPATH_AUTH, self.onAuth)
     281
     282            return
     283
     284        initialResponse = base64.b64decode(unicode(auth))
     285        authzid, authcid, passwd = initialResponse.split('\x00')
     286
     287        # TODO: check passwd
     288
     289        # authenticated
     290
     291        self.username = authcid
     292
     293        success = domish.Element((sasl.NS_XMPP_SASL, 'success'))
     294        self.xmlstream.send(success)
     295        self.xmlstream.reset()
     296
     297        self.toState('bind')
     298
     299
     300    def streamStartedBind(self):
     301        features = domish.Element((xmlstream.NS_STREAMS, 'features'))
     302        features.addElement((client.NS_XMPP_BIND, 'bind'))
     303        features.addElement((client.NS_XMPP_SESSION, 'session'))
     304        self.xmlstream.send(features)
     305        self.xmlstream.addOnetimeObserver(XPATH_BIND, self.onBind)
     306
     307
     308    def doBind(self, iq):
     309        iq.handled = True
     310        self.resource = unicode(iq.bind) or 'default'
     311
     312        # TODO: check for resource conflicts
     313
     314        newJID = JID(tuple=(self.username, self.domain, self.resource))
     315
     316        reply = domish.Element((None, 'iq'))
     317        reply['type'] = 'result'
     318        if iq.getAttribute('id'):
     319            reply['id'] = iq['id']
     320        reply.addElement((client.NS_XMPP_BIND, 'bind'))
     321        reply.bind.addElement((client.NS_XMPP_BIND, 'jid'),
     322                              content=newJID.full())
     323        self.xmlstream.send(reply)
     324        return defer.succeed(newJID)
     325
     326
     327    def onBind(self, iq):
     328        def cb(jid):
     329            self.xmlstream.otherEntity = jid
     330            self.toState('initialized')
     331
     332        def eb(failure):
     333            if not isinstance(failure, error.StanzaError):
     334                log.msg(failure)
     335                exc = error.StanzaError('internal-server-error')
     336            else:
     337                exc = failure.value
     338            self.xmlstream.send(exc.toResponse(iq))
     339
     340        d = defer.maybeDeferred(self.doBind, iq)
     341        d.addCallbacks(cb, eb)
     342
     343
     344    def onSession(self, iq):
     345        iq.handled = True
     346
     347        reply = domish.Element((None, 'iq'))
     348        reply['type'] = 'result'
     349        if iq.getAttribute('id'):
     350            reply['id'] = iq['id']
     351        reply.addElement((client.NS_XMPP_SESSION, 'session'))
     352        self.xmlstream.send(reply)
Note: See TracBrowser for help on using the repository browser.