~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/words/protocols/jabber/component.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mfrom: (1.1.5 upstream) (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20070117145235-btmig6qfmqfen0om
Tags: 2.5.0-0ubuntu1
New upstream version, compatible with python2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.words.test.test_jabbercomponent -*-
 
2
#
 
3
# Copyright (c) 2001-2006 Twisted Matrix Laboratories.
 
4
# See LICENSE for details.
 
5
 
 
6
"""
 
7
External server-side components.
 
8
 
 
9
Most Jabber server implementations allow for add-on components that act as a
 
10
seperate entity on the Jabber network, but use the server-to-server
 
11
functionality of a regular Jabber IM server. These so-called 'external
 
12
components' are connected to the Jabber server using the Jabber Component
 
13
Protocol as defined in U{JEP-0114<http://www.jabber.org/jeps/jep-0114.html>}.
 
14
 
 
15
This module allows for writing external server-side component by assigning one
 
16
or more services implementing L{ijabber.IService} up to L{ServiceManager}. The
 
17
ServiceManager connects to the Jabber server and is responsible for the
 
18
corresponding XML stream.
 
19
"""
 
20
 
 
21
from zope.interface import implements
 
22
 
 
23
from twisted.application import service
 
24
from twisted.internet import defer
 
25
from twisted.words.xish import domish
 
26
from twisted.words.protocols.jabber import ijabber, jstrports, xmlstream
 
27
 
 
28
def componentFactory(componentid, password):
 
29
    """
 
30
    XML stream factory for external server-side components.
 
31
 
 
32
    @param componentid: JID of the component.
 
33
    @type componentid: L{unicode}
 
34
    @param password: password used to authenticate to the server.
 
35
    @type password: L{str}
 
36
    """
 
37
    a = ConnectComponentAuthenticator(componentid, password)
 
38
    return xmlstream.XmlStreamFactory(a)
 
39
 
 
40
class ComponentInitiatingInitializer(object):
 
41
    """
 
42
    External server-side component authentication initializer for the
 
43
    initiating entity.
 
44
 
 
45
    @ivar xmlstream: XML stream between server and component.
 
46
    @type xmlstream: L{xmlstream.XmlStream}
 
47
    """
 
48
 
 
49
    def __init__(self, xs):
 
50
        self.xmlstream = xs
 
51
        self._deferred = None
 
52
 
 
53
    def initialize(self):
 
54
        xs = self.xmlstream
 
55
        hs = domish.Element((self.xmlstream.namespace, "handshake"))
 
56
        hs.addContent(xmlstream.hashPassword(xs.sid,
 
57
                                             xs.authenticator.password))
 
58
 
 
59
        # Setup observer to watch for handshake result
 
60
        xs.addOnetimeObserver("/handshake", self._cbHandshake)
 
61
        xs.send(hs)
 
62
        self._deferred = defer.Deferred()
 
63
        return self._deferred
 
64
 
 
65
    def _cbHandshake(self, _):
 
66
        # we have successfully shaken hands and can now consider this
 
67
        # entity to represent the component JID.
 
68
        self.xmlstream.thisHost = self.xmlstream.otherHost
 
69
        self._deferred.callback(None)
 
70
 
 
71
class ConnectComponentAuthenticator(xmlstream.ConnectAuthenticator):
 
72
    """
 
73
    Authenticator to permit an XmlStream to authenticate against a Jabber
 
74
    server as an external component (where the Authenticator is initiating the
 
75
    stream).
 
76
    """
 
77
    namespace = 'jabber:component:accept'
 
78
 
 
79
    def __init__(self, componentjid, password):
 
80
        """
 
81
        @type componentjid: L{str}
 
82
        @param componentjid: Jabber ID that this component wishes to bind to.
 
83
 
 
84
        @type password: L{str}
 
85
        @param password: Password/secret this component uses to authenticate.
 
86
        """
 
87
        # Note that we are sending 'to' our desired component JID.
 
88
        xmlstream.ConnectAuthenticator.__init__(self, componentjid)
 
89
        self.password = password
 
90
 
 
91
    def associateWithStream(self, xs):
 
92
        xs.version = (0, 0)
 
93
        xmlstream.ConnectAuthenticator.associateWithStream(self, xs)
 
94
 
 
95
        xs.initializers = [ComponentInitiatingInitializer(xs)]
 
96
 
 
97
class ListenComponentAuthenticator(xmlstream.Authenticator):
 
98
    """
 
99
    Placeholder for listening components.
 
100
    """
 
101
 
 
102
class Service(service.Service):
 
103
    """
 
104
    External server-side component service.
 
105
    """
 
106
 
 
107
    implements(ijabber.IService)
 
108
 
 
109
    def componentConnected(self, xs):
 
110
        pass
 
111
 
 
112
    def componentDisconnected(self):
 
113
        pass
 
114
 
 
115
    def transportConnected(self, xs):
 
116
        pass
 
117
 
 
118
    def send(self, obj):
 
119
        """
 
120
        Send data over service parent's XML stream.
 
121
 
 
122
        @note: L{ServiceManager} maintains a queue for data sent using this
 
123
        method when there is no current established XML stream. This data is
 
124
        then sent as soon as a new stream has been established and initialized.
 
125
        Subsequently, L{componentConnected} will be called again. If this
 
126
        queueing is not desired, use C{send} on the XmlStream object (passed to
 
127
        L{componentConnected}) directly.
 
128
 
 
129
        @param obj: data to be sent over the XML stream. This is usually an
 
130
        object providing L{domish.IElement}, or serialized XML. See
 
131
        L{xmlstream.XmlStream} for details.
 
132
        """
 
133
 
 
134
        self.parent.send(obj)
 
135
 
 
136
class ServiceManager(service.MultiService):
 
137
    """
 
138
    Business logic representing a managed component connection to a Jabber
 
139
    router.
 
140
 
 
141
    This service maintains a single connection to a Jabber router and provides
 
142
    facilities for packet routing and transmission. Business logic modules are
 
143
    services implementing L{ijabber.IService} (like subclasses of L{Service}), and
 
144
    added as sub-service.
 
145
    """
 
146
 
 
147
    def __init__(self, jid, password):
 
148
        service.MultiService.__init__(self)
 
149
 
 
150
        # Setup defaults
 
151
        self.jabberId = jid
 
152
        self.xmlstream = None
 
153
 
 
154
        # Internal buffer of packets
 
155
        self._packetQueue = []
 
156
 
 
157
        # Setup the xmlstream factory
 
158
        self._xsFactory = componentFactory(self.jabberId, password)
 
159
 
 
160
        # Register some lambda functions to keep the self.xmlstream var up to
 
161
        # date
 
162
        self._xsFactory.addBootstrap(xmlstream.STREAM_CONNECTED_EVENT,
 
163
                                     self._connected)
 
164
        self._xsFactory.addBootstrap(xmlstream.STREAM_AUTHD_EVENT, self._authd)
 
165
        self._xsFactory.addBootstrap(xmlstream.STREAM_END_EVENT,
 
166
                                     self._disconnected)
 
167
 
 
168
        # Map addBootstrap and removeBootstrap to the underlying factory -- is
 
169
        # this right? I have no clue...but it'll work for now, until i can
 
170
        # think about it more.
 
171
        self.addBootstrap = self._xsFactory.addBootstrap
 
172
        self.removeBootstrap = self._xsFactory.removeBootstrap
 
173
 
 
174
    def getFactory(self):
 
175
        return self._xsFactory
 
176
 
 
177
    def _connected(self, xs):
 
178
        self.xmlstream = xs
 
179
        for c in self:
 
180
            if ijabber.IService.providedBy(c):
 
181
                c.transportConnected(xs)
 
182
 
 
183
    def _authd(self, xs):
 
184
        # Flush all pending packets
 
185
        for p in self._packetQueue:
 
186
            self.xmlstream.send(p)
 
187
        self._packetQueue = []
 
188
 
 
189
        # Notify all child services which implement the IService interface
 
190
        for c in self:
 
191
            if ijabber.IService.providedBy(c):
 
192
                c.componentConnected(xs)
 
193
 
 
194
    def _disconnected(self, _):
 
195
        self.xmlstream = None
 
196
 
 
197
        # Notify all child services which implement
 
198
        # the IService interface
 
199
        for c in self:
 
200
            if ijabber.IService.providedBy(c):
 
201
                c.componentDisconnected()
 
202
 
 
203
    def send(self, obj):
 
204
        """
 
205
        Send data over the XML stream.
 
206
 
 
207
        When there is no established XML stream, the data is queued and sent
 
208
        out when a new XML stream has been established and initialized.
 
209
 
 
210
        @param obj: data to be sent over the XML stream. This is usually an
 
211
        object providing L{domish.IElement}, or serialized XML. See
 
212
        L{xmlstream.XmlStream} for details.
 
213
        """
 
214
 
 
215
        if self.xmlstream != None:
 
216
            self.xmlstream.send(obj)
 
217
        else:
 
218
            self._packetQueue.append(obj)
 
219
 
 
220
def buildServiceManager(jid, password, strport):
 
221
    """
 
222
    Constructs a pre-built L{ServiceManager}, using the specified strport
 
223
    string.
 
224
    """
 
225
 
 
226
    svc = ServiceManager(jid, password)
 
227
    client_svc = jstrports.client(strport, svc.getFactory())
 
228
    client_svc.setServiceParent(svc)
 
229
    return svc