source: wokkel/client.py @ 71:b502670cb872

Last change on this file since 71:b502670cb872 was 71:b502670cb872, checked in by Ralph Meijer <ralphm@…>, 13 years ago

Also have XMPPClient use the new connector.

Re #26.

File size: 4.7 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 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
82class XMPPClient(StreamManager, service.Service):
83    """
84    Service that initiates an XMPP client connection.
85    """
86
87    def __init__(self, jid, password, host=None, port=5222):
88        self.domain = jid.host
89        self.host = host
90        self.port = port
91
92        factory = HybridClientFactory(jid, password)
93
94        StreamManager.__init__(self, factory)
95
96    def startService(self):
97        service.Service.startService(self)
98
99        self._connection = self._getConnection()
100
101    def stopService(self):
102        service.Service.stopService(self)
103
104        self.factory.stopTrying()
105        self._connection.disconnect()
106
107    def initializationFailed(self, reason):
108        """
109        Called when stream initialization has failed.
110
111        Stop the service (thereby disconnecting the current stream) and
112        raise the exception.
113        """
114        self.stopService()
115        reason.raiseException()
116
117    def _getConnection(self):
118        if self.host:
119            return reactor.connectTCP(self.host, self.port, self.factory)
120        else:
121            c = XMPPClientConnector(reactor, self.domain, self.factory)
122            c.connect()
123            return c
124
125
126class DeferredClientFactory(generic.DeferredXmlStreamFactory):
127
128    def __init__(self, jid, password):
129        authenticator = client.XMPPAuthenticator(jid, password)
130        generic.DeferredXmlStreamFactory.__init__(self, authenticator)
131        self.streamManager = StreamManager(self)
132
133
134    def addHandler(self, handler):
135        """
136        Add a subprotocol handler to the stream manager.
137        """
138        self.streamManager.addHandler(handler)
139
140
141    def removeHandler(self, handler):
142        """
143        Add a subprotocol handler to the stream manager.
144        """
145        self.streamManager.removeHandler(handler)
146
147
148
149class XMPPClientConnector(SRVConnector):
150    def __init__(self, reactor, domain, factory):
151        SRVConnector.__init__(self, reactor, 'xmpp-client', domain, factory)
152
153
154    def pickServer(self):
155        host, port = SRVConnector.pickServer(self)
156
157        if not self.servers and not self.orderedServers:
158            # no SRV record, fall back..
159            port = 5222
160
161        return host, port
162
163
164
165def clientCreator(factory):
166    domain = factory.authenticator.jid.host
167    c = XMPPClientConnector(reactor, domain, factory)
168    c.connect()
169    return factory.deferred
Note: See TracBrowser for help on using the repository browser.