source: wokkel/client.py @ 208:0538df970eaf

Last change on this file since 208:0538df970eaf was 208:0538df970eaf, checked in by Ralph Meijer <ralphm@…>, 4 years ago

imported patch py3-client.patch

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