source: wokkel/client.py @ 13:b460ae320c23

Last change on this file since 13:b460ae320c23 was 8:af29fb9001cb, checked in by Ralph Meijer <ralphm@…>, 14 years ago

Add compatiblity module to have wokkel work with Twisted Words 0.5.

Author: ralphm
Fixes #5.

File size: 4.8 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 XmlStreamFactoryMixin
20except ImportError:
21    from wokkel.compat import XmlStreamFactoryMixin
22
23
24from wokkel.subprotocols import StreamManager
25
26class CheckAuthInitializer(object):
27    """
28    Check what authentication methods are available.
29    """
30
31    def __init__(self, xs):
32        self.xmlstream = xs
33
34    def initialize(self):
35        if (sasl.NS_XMPP_SASL, 'mechanisms') in self.xmlstream.features:
36            inits = [(sasl.SASLInitiatingInitializer, True),
37                     (client.BindInitializer, True),
38                     (client.SessionInitializer, False)]
39
40            for initClass, required in inits:
41                init = initClass(self.xmlstream)
42                init.required = required
43                self.xmlstream.initializers.append(init)
44        elif (client.NS_IQ_AUTH_FEATURE, 'auth') in self.xmlstream.features:
45            self.xmlstream.initializers.append(
46                    client.IQAuthInitializer(self.xmlstream))
47        else:
48            raise Exception("No available authentication method found")
49
50
51class HybridAuthenticator(xmlstream.ConnectAuthenticator):
52    """
53    Initializes an XmlStream connecting to an XMPP server as a Client.
54
55    This is similar to L{client.XMPPAuthenticator}, but also tries non-SASL
56    autentication.
57    """
58
59    namespace = 'jabber:client'
60
61    def __init__(self, jid, password):
62        xmlstream.ConnectAuthenticator.__init__(self, jid.host)
63        self.jid = jid
64        self.password = password
65
66    def associateWithStream(self, xs):
67        xmlstream.ConnectAuthenticator.associateWithStream(self, xs)
68
69        tlsInit = xmlstream.TLSInitiatingInitializer(xs)
70        xs.initializers = [client.CheckVersionInitializer(xs),
71                           tlsInit,
72                           CheckAuthInitializer(xs)]
73
74
75def HybridClientFactory(jid, password):
76    """
77    Client factory for XMPP 1.0.
78
79    This is similar to L{client.XMPPClientFactory} but also tries non-SASL
80    autentication.
81    """
82
83    a = HybridAuthenticator(jid, password)
84    return xmlstream.XmlStreamFactory(a)
85
86
87class XMPPClient(StreamManager, service.Service):
88    """
89    Service that initiates an XMPP client connection.
90    """
91
92    def __init__(self, jid, password, host=None, port=5222):
93        self.domain = jid.host
94        self.host = host
95        self.port = port
96
97        factory = HybridClientFactory(jid, password)
98
99        StreamManager.__init__(self, factory)
100
101    def startService(self):
102        service.Service.startService(self)
103
104        self._connection = self._getConnection()
105
106    def stopService(self):
107        service.Service.stopService(self)
108
109        self.factory.stopTrying()
110        self._connection.disconnect()
111
112    def initializationFailed(self, reason):
113        """
114        Called when stream initialization has failed.
115
116        Stop the service (thereby disconnecting the current stream) and
117        raise the exception.
118        """
119        self.stopService()
120        reason.raiseException()
121
122    def _getConnection(self):
123        if self.host:
124            return reactor.connectTCP(self.host, self.port, self.factory)
125        else:
126            c = SRVConnector(reactor, 'xmpp-client', self.domain, self.factory)
127            c.connect()
128            return c
129
130
131class DeferredClientFactory(XmlStreamFactoryMixin, protocol.ClientFactory):
132    protocol = xmlstream.XmlStream
133
134    def __init__(self, jid, password):
135        self.authenticator = client.XMPPAuthenticator(jid, password)
136        XmlStreamFactoryMixin.__init__(self, self.authenticator)
137
138        self.deferred = defer.Deferred()
139
140        self.addBootstrap(xmlstream.STREAM_AUTHD_EVENT, self.deferred.callback)
141        self.addBootstrap(xmlstream.INIT_FAILED_EVENT, self.deferred.errback)
142
143        self.streamManager = StreamManager(self)
144
145    def clientConnectionFailed(self, connector, reason):
146        self.deferred.errback(reason)
147
148    def addHandler(self, handler):
149        """
150        Add a subprotocol handler to the stream manager.
151        """
152        self.streamManager.addHandler(handler)
153
154    def removeHandler(self, handler):
155        """
156        Add a subprotocol handler to the stream manager.
157        """
158        self.streamManager.removeHandler(handler)
159
160
161def clientCreator(factory):
162    domain = factory.authenticator.jid.host
163    c = SRVConnector(reactor, 'xmpp-client', domain, factory)
164    c.connect()
165    return factory.deferred
Note: See TracBrowser for help on using the repository browser.