source: wokkel/client.py @ 27:d62d7ea14995

Last change on this file since 27:d62d7ea14995 was 14:312a60e100c4, checked in by Ralph Meijer <ralphm@…>, 14 years ago

Make sure the deferred is fired by the stream manager, so that the latter
can do its initializations, first.

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