]> code.delx.au - pymsnt/blob - src/tlib/xmlstream.py
Reimporting (0.9.5)
[pymsnt] / src / tlib / xmlstream.py
1 # -*- test-case-name: twisted.test.test_xmlstream -*-
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 twisted.internet import reactor, protocol, defer
20 from twisted.xish import utility
21 import domish
22
23 STREAM_CONNECTED_EVENT = intern("//event/stream/connected")
24 STREAM_START_EVENT = intern("//event/stream/start")
25 STREAM_END_EVENT = intern("//event/stream/end")
26 STREAM_ERROR_EVENT = intern("//event/stream/error")
27 STREAM_AUTHD_EVENT = intern("//event/stream/authd")
28 RAWDATA_IN_EVENT = intern("//event/rawdata/in")
29 RAWDATA_OUT_EVENT = intern("//event/rawdata/out")
30
31 def hashPassword(sid, password):
32 """Create a SHA1-digest string of a session identifier and password """
33 import sha
34 return sha.new("%s%s" % (sid, password)).hexdigest()
35
36 class Authenticator:
37 """ Base class for business logic of authenticating an XmlStream
38
39 Subclass this object to enable an XmlStream to authenticate to different
40 types of stream hosts (such as clients, components, etc.).
41
42 Rules:
43 1. The Authenticator MUST dispatch a L{STREAM_AUTHD_EVENT} when the stream
44 has been completely authenticated.
45 2. The Authenticator SHOULD reset all state information when
46 L{associateWithStream} is called.
47 3. The Authenticator SHOULD override L{streamStarted}, and start
48 authentication there.
49
50
51 @type namespace: C{str}
52 @cvar namespace: Default namespace for the XmlStream
53
54 @type version: C{int}
55 @cvar version: Version attribute for XmlStream. 0.0 will cause the
56 XmlStream to not include a C{version} attribute in the
57 header.
58
59 @type streamHost: C{str}
60 @ivar streamHost: Target host for this stream (used as the 'to' attribute)
61
62 @type xmlstream: C{XmlStream}
63 @ivar xmlstream: The XmlStream that needs authentication
64 """
65
66 namespace = 'invalid' # Default namespace for stream
67 version = 0.0 # Stream version
68
69 def __init__(self, streamHost):
70 self.streamHost = streamHost
71 self.xmlstream = None
72
73 def connectionMade(self):
74 """
75 Called by the XmlStream when the underlying socket connection is
76 in place. This allows the Authenticator to send an initial root
77 element, if it's connecting, or wait for an inbound root from
78 the peer if it's accepting the connection
79
80 Subclasses can use self.xmlstream.send() with the provided xmlstream
81 parameter to send any initial data to the peer
82 """
83
84 def streamStarted(self, rootelem):
85 """
86 Called by the XmlStream when it has received a root element from
87 the connected peer.
88
89 @type rootelem: C{Element}
90 @param rootelem: The root element of the XmlStream received from
91 the streamHost
92 """
93
94 def associateWithStream(self, xmlstream):
95 """
96 Called by the XmlStreamFactory when a connection has been made
97 to the requested peer, and an XmlStream object has been
98 instantiated.
99
100 The default implementation just saves a handle to the new
101 XmlStream.
102
103 @type xmlstream: C{XmlStream}
104 @param xmlstream: The XmlStream that will be passing events to this
105 Authenticator.
106
107 """
108 self.xmlstream = xmlstream
109
110 class ConnectAuthenticator(Authenticator):
111 def connectionMade(self):
112 # Generate stream header
113 if self.version == 1.0:
114 sh = "<stream:stream xmlns='%s' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>" % \
115 (self.namespace)
116 else:
117 sh = "<stream:stream xmlns='%s' xmlns:stream='http://etherx.jabber.org/streams' to='%s'>" % \
118 (self.namespace, self.streamHost)
119 self.xmlstream.send(sh)
120
121 class XmlStream(protocol.Protocol, utility.EventDispatcher):
122 def __init__(self, authenticator):
123 utility.EventDispatcher.__init__(self)
124 self.stream = None
125 self.authenticator = authenticator
126 self.sid = None
127 self.rawDataOutFn = None
128 self.rawDataInFn = None
129
130 # Reset the authenticator
131 authenticator.associateWithStream(self)
132
133 # Setup watcher for stream errors
134 self.addObserver("/error[@xmlns='http://etherx.jabber.org/streams']", self.streamError)
135
136 def streamError(self, errelem):
137 self.dispatch(errelem, STREAM_ERROR_EVENT)
138 self.transport.loseConnection()
139
140 ### --------------------------------------------------------------
141 ###
142 ### Protocol events
143 ###
144 ### --------------------------------------------------------------
145 def connectionMade(self):
146 # Setup the parser
147 self.stream = domish.elementStream()
148 self.stream.DocumentStartEvent = self.onDocumentStart
149 self.stream.ElementEvent = self.onElement
150 self.stream.DocumentEndEvent = self.onDocumentEnd
151
152 self.dispatch(self, STREAM_CONNECTED_EVENT)
153
154 self.authenticator.connectionMade()
155
156 def dataReceived(self, buf):
157 try:
158 if self.rawDataInFn: self.rawDataInFn(buf)
159 self.stream.parse(buf)
160 except domish.ParserError:
161 self.dispatch(self, STREAM_ERROR_EVENT)
162 self.transport.loseConnection()
163
164 def connectionLost(self, _):
165 self.dispatch(self, STREAM_END_EVENT)
166 self.stream = None
167
168 ### --------------------------------------------------------------
169 ###
170 ### DOM events
171 ###
172 ### --------------------------------------------------------------
173 def onDocumentStart(self, rootelem):
174 if rootelem.hasAttribute("id"):
175 self.sid = rootelem["id"] # Extract stream identifier
176 self.authenticator.streamStarted(rootelem) # Notify authenticator
177 self.dispatch(self, STREAM_START_EVENT)
178
179 def onElement(self, element):
180 self.dispatch(element)
181
182 def onDocumentEnd(self):
183 self.transport.loseConnection()
184
185 def setDispatchFn(self, fn):
186 self.stream.ElementEvent = fn
187
188 def resetDispatchFn(self):
189 self.stream.ElementEvent = self.onElement
190
191 def send(self, obj):
192 if isinstance(obj, domish.Element):
193 obj = obj.toXml()
194
195 if self.rawDataOutFn:
196 self.rawDataOutFn(obj)
197
198 self.transport.write(obj)
199
200
201 class XmlStreamFactory(protocol.ReconnectingClientFactory):
202 def __init__(self, authenticator):
203 self.authenticator = authenticator
204 self.bootstraps = []
205
206 def buildProtocol(self, _):
207 self.resetDelay()
208 # Create the stream and register all the bootstrap observers
209 xs = XmlStream(self.authenticator)
210 xs.factory = self
211 for event, fn in self.bootstraps: xs.addObserver(event, fn)
212 return xs
213
214 def addBootstrap(self, event, fn):
215 self.bootstraps.append((event, fn))
216
217 def removeBootstrap(self, event, fn):
218 self.bootstraps.remove((event, fn))
219
220
221
222
223