]>
code.delx.au - pymsnt/blob - src/tlib/jabber/component.py
1 # -*- test-case-name: twisted.test.test_jabbercomponent -*-
3 # Twisted, the Framework of Your Internet
4 # Copyright (C) 2001 Matthew W. Lefkowitz
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.
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.
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
19 from tlib
import domish
20 from twisted
.xish
import xpath
, utility
21 from tlib
import xmlstream
22 from twisted
.protocols
.jabber
import jstrports
24 def componentFactory(componentid
, password
):
25 a
= ConnectComponentAuthenticator(componentid
, password
)
26 return xmlstream
.XmlStreamFactory(a
)
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).
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.
37 namespace
= 'jabber:component:accept'
39 def __init__(self
, componentjid
, password
):
41 @type componentjid: C{str}
42 @param componentjid: Jabber ID that this component wishes to bind to.
44 @type password: C{str}
45 @param password: Password/secret this component uses to authenticate.
47 xmlstream
.ConnectAuthenticator
.__init
__(self
, componentjid
)
48 self
.password
= password
50 def streamStarted(self
, rootelem
):
52 hs
= domish
.Element(("jabber:component:accept", "handshake"))
53 hs
.addContent(xmlstream
.hashPassword(self
.xmlstream
.sid
, self
.password
))
55 # Setup observer to watch for handshake result
56 self
.xmlstream
.addOnetimeObserver("/handshake", self
._handshakeEvent
)
57 self
.xmlstream
.send(hs
)
59 def _handshakeEvent(self
, elem
):
60 self
.xmlstream
.dispatch(self
.xmlstream
, xmlstream
.STREAM_AUTHD_EVENT
)
62 class ListenComponentAuthenticator(xmlstream
.Authenticator
):
63 """ Placeholder for listening components """
67 from twisted
.application
import service
68 from twisted
.python
import components
70 class IService(components
.Interface
):
71 def componentConnected(self
, xmlstream
):
72 """ Parent component has established a connection
75 def componentDisconnected(self
):
76 """ Parent component has lost a connection to the Jabber system
79 def transportConnected(self
, xmlstream
):
80 """ Parent component has established a connection over the underlying transport
83 class Service(service
.Service
):
84 __implements__
= (IService
, )
86 def componentConnected(self
, xmlstream
):
89 def componentDisconnected(self
):
92 def transportConnected(self
, xmlstream
):
98 class ServiceManager(service
.MultiService
):
99 """ Business logic representing a managed component connection to a Jabber router
101 This Service maintains a single connection to a Jabber router and
102 provides facilities for packet routing and transmission. Business
104 subclasses, and added as sub-service.
106 def __init__(self
, jid
, password
):
107 service
.MultiService
.__init
__(self
)
111 self
.xmlstream
= None
113 # Internal buffer of packets
114 self
._packetQueue
= []
116 # Setup the xmlstream factory
117 self
._xsFactory
= componentFactory(self
.jabberId
, password
)
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
)
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
127 self
.addBootstrap
= self
._xsFactory
.addBootstrap
128 self
.removeBootstrap
= self
._xsFactory
.removeBootstrap
130 def getFactory(self
):
131 return self
._xsFactory
133 def _connected(self
, xs
):
136 if components
.implements(c
, IService
):
137 c
.transportConnected(xs
)
139 def _authd(self
, xs
):
140 # Flush all pending packets
141 for p
in self
._packetQueue
:
142 self
.xmlstream
.send(p
)
143 self
._packetQueue
= []
145 # Notify all child services which implement
146 # the IService interface
148 if components
.implements(c
, IService
):
149 c
.componentConnected(xs
)
151 def _disconnected(self
, _
):
152 self
.xmlstream
= None
154 # Notify all child services which implement
155 # the IService interface
157 if components
.implements(c
, IService
):
158 c
.componentDisconnected()
161 if self
.xmlstream
!= None:
162 self
.xmlstream
.send(obj
)
164 self
._packetQueue
.append(obj
)
169 def buildServiceManager(jid
, password
, strport
):
170 """ Constructs a pre-built C{component.ServiceManager}, using the specified strport string.
172 svc
= ServiceManager(jid
, password
)
173 client_svc
= jstrports
.client(strport
, svc
.getFactory())
174 client_svc
.setServiceParent(svc
)