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

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

imported patch py3-client.patch

  • Property exe set to *
File size: 5.2 KB
RevLine 
[8]1# -*- test-case-name: wokkel.test.test_client -*-
2#
[96]3# Copyright (c) Ralph Meijer.
[2]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
[208]13from __future__ import division, absolute_import
14
[2]15from twisted.application import service
[55]16from twisted.internet import reactor
[2]17from twisted.names.srvconnect import SRVConnector
18from twisted.words.protocols.jabber import client, sasl, xmlstream
[8]19
[55]20from wokkel import generic
21from wokkel.subprotocols import StreamManager
[2]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
[72]84
[2]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):
[72]91        self.jid = jid
[184]92        self.domain = jid.host.encode('idna')
[2]93        self.host = host
94        self.port = port
95
96        factory = HybridClientFactory(jid, password)
97
98        StreamManager.__init__(self, factory)
99
[72]100
[2]101    def startService(self):
102        service.Service.startService(self)
103
104        self._connection = self._getConnection()
105
[72]106
[2]107    def stopService(self):
108        service.Service.stopService(self)
109
110        self.factory.stopTrying()
111        self._connection.disconnect()
112
[72]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
[2]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
[72]136
[2]137    def _getConnection(self):
138        if self.host:
139            return reactor.connectTCP(self.host, self.port, self.factory)
140        else:
[71]141            c = XMPPClientConnector(reactor, self.domain, self.factory)
[2]142            c.connect()
143            return c
144
145
[72]146
[55]147class DeferredClientFactory(generic.DeferredXmlStreamFactory):
[2]148
149    def __init__(self, jid, password):
[55]150        authenticator = client.XMPPAuthenticator(jid, password)
151        generic.DeferredXmlStreamFactory.__init__(self, authenticator)
[61]152        self.streamManager = StreamManager(self)
[2]153
[34]154
[2]155    def addHandler(self, handler):
156        """
157        Add a subprotocol handler to the stream manager.
158        """
159        self.streamManager.addHandler(handler)
160
[34]161
[2]162    def removeHandler(self, handler):
163        """
164        Add a subprotocol handler to the stream manager.
165        """
166        self.streamManager.removeHandler(handler)
167
168
[55]169
[70]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
[2]186def clientCreator(factory):
[184]187    domain = factory.authenticator.jid.host.encode('idna')
[70]188    c = XMPPClientConnector(reactor, domain, factory)
[2]189    c.connect()
190    return factory.deferred
Note: See TracBrowser for help on using the repository browser.