source: wokkel/client.py @ 55:a8cda961ee7e

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

Implement server-to-server dialback protocol and stream management.

Author: ralphm.
Fixes #33.

This also abstracts the concept of a client factory that fires a deferred.

File size: 4.3 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 = SRVConnector(reactor, 'xmpp-client', 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
132
133    def addHandler(self, handler):
134        """
135        Add a subprotocol handler to the stream manager.
136        """
137        self.streamManager.addHandler(handler)
138
139
140    def removeHandler(self, handler):
141        """
142        Add a subprotocol handler to the stream manager.
143        """
144        self.streamManager.removeHandler(handler)
145
146
147
148def clientCreator(factory):
149    domain = factory.authenticator.jid.host
150    c = SRVConnector(reactor, 'xmpp-client', domain, factory)
151    c.connect()
152    return factory.deferred
Note: See TracBrowser for help on using the repository browser.