source: wokkel/client.py @ 184:a9ab7d3945e2

Last change on this file since 184:a9ab7d3945e2 was 184:a9ab7d3945e2, checked in by Ralph Meijer <ralphm@…>, 7 years ago

Deprecate prepareIDNName.

C{unicode.encode('idna')} does exactly the same as the recently
introduced L{generic.prepareIDNName}.

  • Property exe set to *
File size: 5.2 KB
Line 
1# -*- test-case-name: wokkel.test.test_client -*-
2#
3# Copyright (c) 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 reactor
15from twisted.names.srvconnect import SRVConnector
16from twisted.words.protocols.jabber import client, sasl, xmlstream
17
18from wokkel import generic
19from wokkel.subprotocols import StreamManager
20
21class CheckAuthInitializer(object):
22    """
23    Check what authentication methods are available.
24    """
25
26    def __init__(self, xs):
27        self.xmlstream = xs
28
29    def initialize(self):
30        if (sasl.NS_XMPP_SASL, 'mechanisms') in self.xmlstream.features:
31            inits = [(sasl.SASLInitiatingInitializer, True),
32                     (client.BindInitializer, True),
33                     (client.SessionInitializer, False)]
34
35            for initClass, required in inits:
36                init = initClass(self.xmlstream)
37                init.required = required
38                self.xmlstream.initializers.append(init)
39        elif (client.NS_IQ_AUTH_FEATURE, 'auth') in self.xmlstream.features:
40            self.xmlstream.initializers.append(
41                    client.IQAuthInitializer(self.xmlstream))
42        else:
43            raise Exception("No available authentication method found")
44
45
46class HybridAuthenticator(xmlstream.ConnectAuthenticator):
47    """
48    Initializes an XmlStream connecting to an XMPP server as a Client.
49
50    This is similar to L{client.XMPPAuthenticator}, but also tries non-SASL
51    autentication.
52    """
53
54    namespace = 'jabber:client'
55
56    def __init__(self, jid, password):
57        xmlstream.ConnectAuthenticator.__init__(self, jid.host)
58        self.jid = jid
59        self.password = password
60
61    def associateWithStream(self, xs):
62        xmlstream.ConnectAuthenticator.associateWithStream(self, xs)
63
64        tlsInit = xmlstream.TLSInitiatingInitializer(xs)
65        xs.initializers = [client.CheckVersionInitializer(xs),
66                           tlsInit,
67                           CheckAuthInitializer(xs)]
68
69
70def HybridClientFactory(jid, password):
71    """
72    Client factory for XMPP 1.0.
73
74    This is similar to L{client.XMPPClientFactory} but also tries non-SASL
75    autentication.
76    """
77
78    a = HybridAuthenticator(jid, password)
79    return xmlstream.XmlStreamFactory(a)
80
81
82
83class XMPPClient(StreamManager, service.Service):
84    """
85    Service that initiates an XMPP client connection.
86    """
87
88    def __init__(self, jid, password, host=None, port=5222):
89        self.jid = jid
90        self.domain = jid.host.encode('idna')
91        self.host = host
92        self.port = port
93
94        factory = HybridClientFactory(jid, password)
95
96        StreamManager.__init__(self, factory)
97
98
99    def startService(self):
100        service.Service.startService(self)
101
102        self._connection = self._getConnection()
103
104
105    def stopService(self):
106        service.Service.stopService(self)
107
108        self.factory.stopTrying()
109        self._connection.disconnect()
110
111
112    def _authd(self, xs):
113        """
114        Called when the stream has been initialized.
115
116        Save the JID that we were assigned by the server, as the resource might
117        differ from the JID we asked for. This is stored on the authenticator
118        by its constituent initializers.
119        """
120        self.jid = self.factory.authenticator.jid
121        StreamManager._authd(self, xs)
122
123
124    def initializationFailed(self, reason):
125        """
126        Called when stream initialization has failed.
127
128        Stop the service (thereby disconnecting the current stream) and
129        raise the exception.
130        """
131        self.stopService()
132        reason.raiseException()
133
134
135    def _getConnection(self):
136        if self.host:
137            return reactor.connectTCP(self.host, self.port, self.factory)
138        else:
139            c = XMPPClientConnector(reactor, self.domain, self.factory)
140            c.connect()
141            return c
142
143
144
145class DeferredClientFactory(generic.DeferredXmlStreamFactory):
146
147    def __init__(self, jid, password):
148        authenticator = client.XMPPAuthenticator(jid, password)
149        generic.DeferredXmlStreamFactory.__init__(self, authenticator)
150        self.streamManager = StreamManager(self)
151
152
153    def addHandler(self, handler):
154        """
155        Add a subprotocol handler to the stream manager.
156        """
157        self.streamManager.addHandler(handler)
158
159
160    def removeHandler(self, handler):
161        """
162        Add a subprotocol handler to the stream manager.
163        """
164        self.streamManager.removeHandler(handler)
165
166
167
168class XMPPClientConnector(SRVConnector):
169    def __init__(self, reactor, domain, factory):
170        SRVConnector.__init__(self, reactor, 'xmpp-client', domain, factory)
171
172
173    def pickServer(self):
174        host, port = SRVConnector.pickServer(self)
175
176        if not self.servers and not self.orderedServers:
177            # no SRV record, fall back..
178            port = 5222
179
180        return host, port
181
182
183
184def clientCreator(factory):
185    domain = factory.authenticator.jid.host.encode('idna')
186    c = XMPPClientConnector(reactor, domain, factory)
187    c.connect()
188    return factory.deferred
Note: See TracBrowser for help on using the repository browser.