1 # -*- test-case-name: twisted.test.test_banana -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
6 Banana -- s-exp based protocol.
8 Future Plans: This module is almost entirely stable. The same caveat applies
9 to it as applies to L{twisted.spread.jelly}, however. Read its future plans
12 @author: Glyph Lefkowitz
15 import copy, cStringIO, struct
17 from twisted.internet import protocol
18 from twisted.persisted import styles
19 from twisted.python import log
21 class BananaError(Exception):
24 def int2b128(integer, stream):
28 assert integer > 0, "can only encode positive integers"
30 stream(chr(integer & 0x7f))
31 integer = integer >> 7
36 Convert an integer represented as a base 128 string into an C{int} or
39 @param st: The integer encoded in a string.
42 @return: The integer value extracted from the string.
43 @rtype: C{int} or C{long}
54 # delimiter characters.
60 # "optional" -- these might be refused by a low-level implementation.
63 # really optional; this is is part of the 'pb' vocabulary
66 HIGH_BIT_SET = chr(0x80)
68 def setPrefixLimit(limit):
70 Set the limit on the prefix length for all Banana connections
71 established after this call.
73 The prefix length limit determines how many bytes of prefix a banana
74 decoder will allow before rejecting a potential object as too large.
77 @param limit: The number of bytes of prefix for banana to allow when
84 SIZE_LIMIT = 640 * 1024 # 640k is all you'll ever need :-)
86 class Banana(protocol.Protocol, styles.Ephemeral):
87 knownDialects = ["pb", "none"]
90 sizeLimit = SIZE_LIMIT
92 def setPrefixLimit(self, limit):
94 Set the prefix limit for decoding done by this protocol instance.
96 @see: L{setPrefixLimit}
98 self.prefixLimit = limit
99 self._smallestLongInt = -2 ** (limit * 7) + 1
100 self._smallestInt = -2 ** 31
101 self._largestInt = 2 ** 31 - 1
102 self._largestLongInt = 2 ** (limit * 7) - 1
105 def connectionReady(self):
106 """Surrogate for connectionMade
107 Called after protocol negotiation.
110 def _selectDialect(self, dialect):
111 self.currentDialect = dialect
112 self.connectionReady()
114 def callExpressionReceived(self, obj):
115 if self.currentDialect:
116 self.expressionReceived(obj)
118 # this is the first message we've received
120 # if I'm a client I have to respond
121 for serverVer in obj:
122 if serverVer in self.knownDialects:
123 self.sendEncoded(serverVer)
124 self._selectDialect(serverVer)
127 # I can't speak any of those dialects.
128 log.msg("The client doesn't speak any of the protocols "
129 "offered by the server: disconnecting.")
130 self.transport.loseConnection()
132 if obj in self.knownDialects:
133 self._selectDialect(obj)
135 # the client just selected a protocol that I did not suggest.
136 log.msg("The client selected a protocol the server didn't "
137 "suggest and doesn't know: disconnecting.")
138 self.transport.loseConnection()
141 def connectionMade(self):
142 self.setPrefixLimit(_PREFIX_LIMIT)
143 self.currentDialect = None
144 if not self.isClient:
145 self.sendEncoded(self.knownDialects)
148 def gotItem(self, item):
151 l[-1][1].append(item)
153 self.callExpressionReceived(item)
157 def dataReceived(self, chunk):
158 buffer = self.buffer + chunk
159 listStack = self.listStack
160 gotItem = self.gotItem
162 assert self.buffer != buffer, "This ain't right: %s %s" % (repr(self.buffer), repr(buffer))
166 if ch >= HIGH_BIT_SET:
170 if pos > self.prefixLimit:
171 raise BananaError("Security precaution: more than %d bytes of prefix" % (self.prefixLimit,))
174 typebyte = buffer[pos]
175 rest = buffer[pos+1:]
176 if len(num) > self.prefixLimit:
177 raise BananaError("Security precaution: longer than %d bytes worth of prefix" % (self.prefixLimit,))
181 raise BananaError("Security precaution: List too long.")
182 listStack.append((num, []))
184 elif typebyte == STRING:
187 raise BananaError("Security precaution: String too long.")
193 elif typebyte == INT:
197 elif typebyte == LONGINT:
201 elif typebyte == LONGNEG:
205 elif typebyte == NEG:
209 elif typebyte == VOCAB:
212 gotItem(self.incomingVocabulary[num])
213 elif typebyte == FLOAT:
216 gotItem(struct.unpack("!d", rest[:8])[0])
220 raise NotImplementedError(("Invalid Type Byte %r" % (typebyte,)))
221 while listStack and (len(listStack[-1][1]) == listStack[-1][0]):
222 item = listStack.pop()[1]
227 def expressionReceived(self, lst):
228 """Called when an expression (list, string, or int) is received.
230 raise NotImplementedError()
233 outgoingVocabulary = {
246 'unpersistable' : 12,
256 # PB Protocol Messages
262 'not_logged_in' : 24,
272 incomingVocabulary = {}
273 for k, v in outgoingVocabulary.items():
274 incomingVocabulary[v] = k
276 def __init__(self, isClient=1):
278 self.outgoingSymbols = copy.copy(self.outgoingVocabulary)
279 self.outgoingSymbolCount = 0
280 self.isClient = isClient
282 def sendEncoded(self, obj):
283 io = cStringIO.StringIO()
284 self._encode(obj, io.write)
285 value = io.getvalue()
286 self.transport.write(value)
288 def _encode(self, obj, write):
289 if isinstance(obj, (list, tuple)):
290 if len(obj) > SIZE_LIMIT:
292 "list/tuple is too long to send (%d)" % (len(obj),))
293 int2b128(len(obj), write)
296 self._encode(elem, write)
297 elif isinstance(obj, (int, long)):
298 if obj < self._smallestLongInt or obj > self._largestLongInt:
300 "int/long is too large to send (%d)" % (obj,))
301 if obj < self._smallestInt:
302 int2b128(-obj, write)
305 int2b128(-obj, write)
307 elif obj <= self._largestInt:
313 elif isinstance(obj, float):
315 write(struct.pack("!d", obj))
316 elif isinstance(obj, str):
317 # TODO: an API for extending banana...
318 if self.currentDialect == "pb" and obj in self.outgoingSymbols:
319 symbolID = self.outgoingSymbols[obj]
320 int2b128(symbolID, write)
323 if len(obj) > SIZE_LIMIT:
325 "string is too long to send (%d)" % (len(obj),))
326 int2b128(len(obj), write)
330 raise BananaError("could not send object: %r" % (obj,))
333 # For use from the interactive interpreter
336 _i._selectDialect("none")
340 """Encode a list s-expression."""
341 io = cStringIO.StringIO()
349 Decode a banana-encoded string.
352 _i.expressionReceived = l.append
357 del _i.expressionReceived