source: wokkel/client.py @ 2:47f1cb624f14

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

Add in pubsub client support, client helpers and generic XMPP subprotocol handlers.

File size: 4.7 KB
Line 
1# Copyright (c) 2003-2007 Ralph Meijer
2# See LICENSE for details.
3
4"""
5XMPP Client support.
6
7This module holds several improvements on top of Twisted's XMPP support
8that should probably eventually move there.
9"""
10
11from twisted.application import service
12from twisted.internet import defer, protocol, reactor
13from twisted.names.srvconnect import SRVConnector
14from twisted.words.protocols.jabber import client, sasl, xmlstream
15from twisted.words.xish.xmlstream import XmlStreamFactoryMixin
16
17from wokkel.subprotocols import StreamManager
18
19class CheckAuthInitializer(object):
20    """
21    Check what authentication methods are available.
22    """
23
24    def __init__(self, xs):
25        self.xmlstream = xs
26
27    def initialize(self):
28        if (sasl.NS_XMPP_SASL, 'mechanisms') in self.xmlstream.features:
29            inits = [(sasl.SASLInitiatingInitializer, True),
30                     (client.BindInitializer, True),
31                     (client.SessionInitializer, False)]
32
33            for initClass, required in inits:
34                init = initClass(self.xmlstream)
35                init.required = required
36                self.xmlstream.initializers.append(init)
37        elif (client.NS_IQ_AUTH_FEATURE, 'auth') in self.xmlstream.features:
38            self.xmlstream.initializers.append(
39                    client.IQAuthInitializer(self.xmlstream))
40        else:
41            raise Exception("No available authentication method found")
42
43
44class HybridAuthenticator(xmlstream.ConnectAuthenticator):
45    """
46    Initializes an XmlStream connecting to an XMPP server as a Client.
47
48    This is similar to L{client.XMPPAuthenticator}, but also tries non-SASL
49    autentication.
50    """
51
52    namespace = 'jabber:client'
53
54    def __init__(self, jid, password):
55        xmlstream.ConnectAuthenticator.__init__(self, jid.host)
56        self.jid = jid
57        self.password = password
58
59    def associateWithStream(self, xs):
60        xmlstream.ConnectAuthenticator.associateWithStream(self, xs)
61
62        tlsInit = xmlstream.TLSInitiatingInitializer(xs)
63        xs.initializers = [client.CheckVersionInitializer(xs),
64                           tlsInit,
65                           CheckAuthInitializer(xs)]
66
67
68def HybridClientFactory(jid, password):
69    """
70    Client factory for XMPP 1.0.
71
72    This is similar to L{client.XMPPClientFactory} but also tries non-SASL
73    autentication.
74    """
75
76    a = HybridAuthenticator(jid, password)
77    return xmlstream.XmlStreamFactory(a)
78
79
80class XMPPClient(StreamManager, service.Service):
81    """
82    Service that initiates an XMPP client connection.
83    """
84
85    def __init__(self, jid, password, host=None, port=5222):
86        self.domain = jid.host
87        self.host = host
88        self.port = port
89
90        factory = HybridClientFactory(jid, password)
91
92        StreamManager.__init__(self, factory)
93
94    def startService(self):
95        service.Service.startService(self)
96
97        self._connection = self._getConnection()
98
99    def stopService(self):
100        service.Service.stopService(self)
101
102        self.factory.stopTrying()
103        self._connection.disconnect()
104
105    def initializationFailed(self, reason):
106        """
107        Called when stream initialization has failed.
108
109        Stop the service (thereby disconnecting the current stream) and
110        raise the exception.
111        """
112        self.stopService()
113        reason.raiseException()
114
115    def _getConnection(self):
116        if self.host:
117            return reactor.connectTCP(self.host, self.port, self.factory)
118        else:
119            c = SRVConnector(reactor, 'xmpp-client', self.domain, self.factory)
120            c.connect()
121            return c
122
123
124class DeferredClientFactory(XmlStreamFactoryMixin, protocol.ClientFactory):
125    protocol = xmlstream.XmlStream
126
127    def __init__(self, jid, password):
128        self.authenticator = client.XMPPAuthenticator(jid, password)
129        XmlStreamFactoryMixin.__init__(self, self.authenticator)
130
131        self.deferred = defer.Deferred()
132
133        self.addBootstrap(xmlstream.STREAM_AUTHD_EVENT, self.deferred.callback)
134        self.addBootstrap(xmlstream.INIT_FAILED_EVENT, self.deferred.errback)
135
136        self.streamManager = StreamManager(self)
137
138    def clientConnectionFailed(self, connector, reason):
139        self.deferred.errback(reason)
140
141    def addHandler(self, handler):
142        """
143        Add a subprotocol handler to the stream manager.
144        """
145        self.streamManager.addHandler(handler)
146
147    def removeHandler(self, handler):
148        """
149        Add a subprotocol handler to the stream manager.
150        """
151        self.streamManager.removeHandler(handler)
152
153
154def clientCreator(factory):
155    domain = factory.authenticator.jid.host
156    c = SRVConnector(reactor, 'xmpp-client', domain, factory)
157    c.connect()
158    return factory.deferred
Note: See TracBrowser for help on using the repository browser.