]> code.delx.au - pymsnt/blob - src/tlib/jabber/component.py
Oops. XDB fixes fixed.
[pymsnt] / src / tlib / jabber / component.py
1 # -*- test-case-name: twisted.test.test_jabbercomponent -*-
2 #
3 # Twisted, the Framework of Your Internet
4 # Copyright (C) 2001 Matthew W. Lefkowitz
5 #
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of version 2.1 of the GNU Lesser General Public
8 # License as published by the Free Software Foundation.
9 #
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 from tlib import domish
20 from twisted.xish import xpath, utility
21 from tlib import xmlstream
22 from twisted.protocols.jabber import jstrports
23
24 def componentFactory(componentid, password):
25 a = ConnectComponentAuthenticator(componentid, password)
26 return xmlstream.XmlStreamFactory(a)
27
28 class ConnectComponentAuthenticator(xmlstream.ConnectAuthenticator):
29 """ Authenticator to permit an XmlStream to authenticate against a Jabber
30 Server as a Component (where the Authenticator is initiating the stream).
31
32 This implements the basic component authentication. Unfortunately this
33 protocol is not formally described anywhere. Fortunately, all the Jabber
34 servers I know of use this mechanism in exactly the same way.
35
36 """
37 namespace = 'jabber:component:accept'
38
39 def __init__(self, componentjid, password):
40 """
41 @type componentjid: C{str}
42 @param componentjid: Jabber ID that this component wishes to bind to.
43
44 @type password: C{str}
45 @param password: Password/secret this component uses to authenticate.
46 """
47 xmlstream.ConnectAuthenticator.__init__(self, componentjid)
48 self.password = password
49
50 def streamStarted(self, rootelem):
51 # Create handshake
52 hs = domish.Element(("jabber:component:accept", "handshake"))
53 hs.addContent(xmlstream.hashPassword(self.xmlstream.sid, self.password))
54
55 # Setup observer to watch for handshake result
56 self.xmlstream.addOnetimeObserver("/handshake", self._handshakeEvent)
57 self.xmlstream.send(hs)
58
59 def _handshakeEvent(self, elem):
60 self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
61
62 class ListenComponentAuthenticator(xmlstream.Authenticator):
63 """ Placeholder for listening components """
64 pass
65
66
67 from twisted.application import service
68 from twisted.python import components
69
70 class IService(components.Interface):
71 def componentConnected(self, xmlstream):
72 """ Parent component has established a connection
73 """
74
75 def componentDisconnected(self):
76 """ Parent component has lost a connection to the Jabber system
77 """
78
79 def transportConnected(self, xmlstream):
80 """ Parent component has established a connection over the underlying transport
81 """
82
83 class Service(service.Service):
84 __implements__ = (IService, )
85
86 def componentConnected(self, xmlstream):
87 pass
88
89 def componentDisconnected(self):
90 pass
91
92 def transportConnected(self, xmlstream):
93 pass
94
95 def send(self, obj):
96 self.parent.send(obj)
97
98 class ServiceManager(service.MultiService):
99 """ Business logic representing a managed component connection to a Jabber router
100
101 This Service maintains a single connection to a Jabber router and
102 provides facilities for packet routing and transmission. Business
103 logic modules can
104 subclasses, and added as sub-service.
105 """
106 def __init__(self, jid, password):
107 service.MultiService.__init__(self)
108
109 # Setup defaults
110 self.jabberId = jid
111 self.xmlstream = None
112
113 # Internal buffer of packets
114 self._packetQueue = []
115
116 # Setup the xmlstream factory
117 self._xsFactory = componentFactory(self.jabberId, password)
118
119 # Register some lambda functions to keep the self.xmlstream var up to date
120 self._xsFactory.addBootstrap(xmlstream.STREAM_CONNECTED_EVENT, self._connected)
121 self._xsFactory.addBootstrap(xmlstream.STREAM_AUTHD_EVENT, self._authd)
122 self._xsFactory.addBootstrap(xmlstream.STREAM_END_EVENT, self._disconnected)
123
124 # Map addBootstrap and removeBootstrap to the underlying factory -- is this
125 # right? I have no clue...but it'll work for now, until i can think about it
126 # more.
127 self.addBootstrap = self._xsFactory.addBootstrap
128 self.removeBootstrap = self._xsFactory.removeBootstrap
129
130 def getFactory(self):
131 return self._xsFactory
132
133 def _connected(self, xs):
134 self.xmlstream = xs
135 for c in self:
136 if components.implements(c, IService):
137 c.transportConnected(xs)
138
139 def _authd(self, xs):
140 # Flush all pending packets
141 for p in self._packetQueue:
142 self.xmlstream.send(p)
143 self._packetQueue = []
144
145 # Notify all child services which implement
146 # the IService interface
147 for c in self:
148 if components.implements(c, IService):
149 c.componentConnected(xs)
150
151 def _disconnected(self, _):
152 self.xmlstream = None
153
154 # Notify all child services which implement
155 # the IService interface
156 for c in self:
157 if components.implements(c, IService):
158 c.componentDisconnected()
159
160 def send(self, obj):
161 if self.xmlstream != None:
162 self.xmlstream.send(obj)
163 else:
164 self._packetQueue.append(obj)
165
166
167
168
169 def buildServiceManager(jid, password, strport):
170 """ Constructs a pre-built C{component.ServiceManager}, using the specified strport string.
171 """
172 svc = ServiceManager(jid, password)
173 client_svc = jstrports.client(strport, svc.getFactory())
174 client_svc.setServiceParent(svc)
175 return svc