source: wokkel/client.py @ 52:118fafcc5c5b

Last change on this file since 52:118fafcc5c5b was 42:93675ba90f36, checked in by Ralph Meijer <ralphm@…>, 14 years ago

Change clientCreator to use the JID passed to the factory correctly.

Author: ralphm
Fixes #37.

File size: 5.3 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 BootstrapMixin
20except ImportError:
21    from wokkel.compat import BootstrapMixin
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(BootstrapMixin, protocol.ClientFactory):
131    protocol = xmlstream.XmlStream
132
133    def __init__(self, jid, password):
134        BootstrapMixin.__init__(self)
135
136        self.jid = jid
137        self.password = password
138
139        deferred = defer.Deferred()
140        self.deferred = deferred
141        self.addBootstrap(xmlstream.INIT_FAILED_EVENT, deferred.errback)
142
143        class ConnectionInitializedHandler(XMPPHandler):
144            def connectionInitialized(self):
145                deferred.callback(None)
146
147        self.streamManager = StreamManager(self)
148        self.addHandler(ConnectionInitializedHandler())
149
150
151    def buildProtocol(self, addr):
152        """
153        Create an instance of XmlStream.
154
155        A new authenticator instance will be created and passed to the new
156        XmlStream. Registered bootstrap event observers are installed as well.
157        """
158        self.authenticator = client.XMPPAuthenticator(self.jid, self.password)
159        xs = self.protocol(self.authenticator)
160        xs.factory = self
161        self.installBootstraps(xs)
162        return xs
163
164
165    def clientConnectionFailed(self, connector, reason):
166        self.deferred.errback(reason)
167
168
169    def addHandler(self, handler):
170        """
171        Add a subprotocol handler to the stream manager.
172        """
173        self.streamManager.addHandler(handler)
174
175
176    def removeHandler(self, handler):
177        """
178        Add a subprotocol handler to the stream manager.
179        """
180        self.streamManager.removeHandler(handler)
181
182
183def clientCreator(factory):
184    domain = factory.jid.host
185    c = SRVConnector(reactor, 'xmpp-client', domain, factory)
186    c.connect()
187    return factory.deferred
Note: See TracBrowser for help on using the repository browser.