Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / spread / banana.py
1 # -*- test-case-name: twisted.test.test_banana -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """
6 Banana -- s-exp based protocol.
7
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
10 for more details.
11
12 @author: Glyph Lefkowitz
13 """
14
15 import copy, cStringIO, struct
16
17 from twisted.internet import protocol
18 from twisted.persisted import styles
19 from twisted.python import log
20
21 class BananaError(Exception):
22     pass
23
24 def int2b128(integer, stream):
25     if integer == 0:
26         stream(chr(0))
27         return
28     assert integer > 0, "can only encode positive integers"
29     while integer:
30         stream(chr(integer & 0x7f))
31         integer = integer >> 7
32
33
34 def b1282int(st):
35     """
36     Convert an integer represented as a base 128 string into an C{int} or
37     C{long}.
38
39     @param st: The integer encoded in a string.
40     @type st: C{str}
41
42     @return: The integer value extracted from the string.
43     @rtype: C{int} or C{long}
44     """
45     e = 1
46     i = 0
47     for char in st:
48         n = ord(char)
49         i += (n * e)
50         e <<= 7
51     return i
52
53
54 # delimiter characters.
55 LIST     = chr(0x80)
56 INT      = chr(0x81)
57 STRING   = chr(0x82)
58 NEG      = chr(0x83)
59 FLOAT    = chr(0x84)
60 # "optional" -- these might be refused by a low-level implementation.
61 LONGINT  = chr(0x85)
62 LONGNEG  = chr(0x86)
63 # really optional; this is is part of the 'pb' vocabulary
64 VOCAB    = chr(0x87)
65
66 HIGH_BIT_SET = chr(0x80)
67
68 def setPrefixLimit(limit):
69     """
70     Set the limit on the prefix length for all Banana connections
71     established after this call.
72
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.
75
76     @type limit: C{int}
77     @param limit: The number of bytes of prefix for banana to allow when
78     decoding.
79     """
80     global _PREFIX_LIMIT
81     _PREFIX_LIMIT = limit
82 setPrefixLimit(64)
83
84 SIZE_LIMIT = 640 * 1024   # 640k is all you'll ever need :-)
85
86 class Banana(protocol.Protocol, styles.Ephemeral):
87     knownDialects = ["pb", "none"]
88
89     prefixLimit = None
90     sizeLimit = SIZE_LIMIT
91
92     def setPrefixLimit(self, limit):
93         """
94         Set the prefix limit for decoding done by this protocol instance.
95
96         @see: L{setPrefixLimit}
97         """
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
103
104
105     def connectionReady(self):
106         """Surrogate for connectionMade
107         Called after protocol negotiation.
108         """
109
110     def _selectDialect(self, dialect):
111         self.currentDialect = dialect
112         self.connectionReady()
113
114     def callExpressionReceived(self, obj):
115         if self.currentDialect:
116             self.expressionReceived(obj)
117         else:
118             # this is the first message we've received
119             if self.isClient:
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)
125                         break
126                 else:
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()
131             else:
132                 if obj in self.knownDialects:
133                     self._selectDialect(obj)
134                 else:
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()
139
140
141     def connectionMade(self):
142         self.setPrefixLimit(_PREFIX_LIMIT)
143         self.currentDialect = None
144         if not self.isClient:
145             self.sendEncoded(self.knownDialects)
146
147
148     def gotItem(self, item):
149         l = self.listStack
150         if l:
151             l[-1][1].append(item)
152         else:
153             self.callExpressionReceived(item)
154
155     buffer = ''
156
157     def dataReceived(self, chunk):
158         buffer = self.buffer + chunk
159         listStack = self.listStack
160         gotItem = self.gotItem
161         while buffer:
162             assert self.buffer != buffer, "This ain't right: %s %s" % (repr(self.buffer), repr(buffer))
163             self.buffer = buffer
164             pos = 0
165             for ch in buffer:
166                 if ch >= HIGH_BIT_SET:
167                     break
168                 pos = pos + 1
169             else:
170                 if pos > self.prefixLimit:
171                     raise BananaError("Security precaution: more than %d bytes of prefix" % (self.prefixLimit,))
172                 return
173             num = buffer[:pos]
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,))
178             if typebyte == LIST:
179                 num = b1282int(num)
180                 if num > SIZE_LIMIT:
181                     raise BananaError("Security precaution: List too long.")
182                 listStack.append((num, []))
183                 buffer = rest
184             elif typebyte == STRING:
185                 num = b1282int(num)
186                 if num > SIZE_LIMIT:
187                     raise BananaError("Security precaution: String too long.")
188                 if len(rest) >= num:
189                     buffer = rest[num:]
190                     gotItem(rest[:num])
191                 else:
192                     return
193             elif typebyte == INT:
194                 buffer = rest
195                 num = b1282int(num)
196                 gotItem(num)
197             elif typebyte == LONGINT:
198                 buffer = rest
199                 num = b1282int(num)
200                 gotItem(num)
201             elif typebyte == LONGNEG:
202                 buffer = rest
203                 num = b1282int(num)
204                 gotItem(-num)
205             elif typebyte == NEG:
206                 buffer = rest
207                 num = -b1282int(num)
208                 gotItem(num)
209             elif typebyte == VOCAB:
210                 buffer = rest
211                 num = b1282int(num)
212                 gotItem(self.incomingVocabulary[num])
213             elif typebyte == FLOAT:
214                 if len(rest) >= 8:
215                     buffer = rest[8:]
216                     gotItem(struct.unpack("!d", rest[:8])[0])
217                 else:
218                     return
219             else:
220                 raise NotImplementedError(("Invalid Type Byte %r" % (typebyte,)))
221             while listStack and (len(listStack[-1][1]) == listStack[-1][0]):
222                 item = listStack.pop()[1]
223                 gotItem(item)
224         self.buffer = ''
225
226
227     def expressionReceived(self, lst):
228         """Called when an expression (list, string, or int) is received.
229         """
230         raise NotImplementedError()
231
232
233     outgoingVocabulary = {
234         # Jelly Data Types
235         'None'           :  1,
236         'class'          :  2,
237         'dereference'    :  3,
238         'reference'      :  4,
239         'dictionary'     :  5,
240         'function'       :  6,
241         'instance'       :  7,
242         'list'           :  8,
243         'module'         :  9,
244         'persistent'     : 10,
245         'tuple'          : 11,
246         'unpersistable'  : 12,
247
248         # PB Data Types
249         'copy'           : 13,
250         'cache'          : 14,
251         'cached'         : 15,
252         'remote'         : 16,
253         'local'          : 17,
254         'lcache'         : 18,
255
256         # PB Protocol Messages
257         'version'        : 19,
258         'login'          : 20,
259         'password'       : 21,
260         'challenge'      : 22,
261         'logged_in'      : 23,
262         'not_logged_in'  : 24,
263         'cachemessage'   : 25,
264         'message'        : 26,
265         'answer'         : 27,
266         'error'          : 28,
267         'decref'         : 29,
268         'decache'        : 30,
269         'uncache'        : 31,
270         }
271
272     incomingVocabulary = {}
273     for k, v in outgoingVocabulary.items():
274         incomingVocabulary[v] = k
275
276     def __init__(self, isClient=1):
277         self.listStack = []
278         self.outgoingSymbols = copy.copy(self.outgoingVocabulary)
279         self.outgoingSymbolCount = 0
280         self.isClient = isClient
281
282     def sendEncoded(self, obj):
283         io = cStringIO.StringIO()
284         self._encode(obj, io.write)
285         value = io.getvalue()
286         self.transport.write(value)
287
288     def _encode(self, obj, write):
289         if isinstance(obj, (list, tuple)):
290             if len(obj) > SIZE_LIMIT:
291                 raise BananaError(
292                     "list/tuple is too long to send (%d)" % (len(obj),))
293             int2b128(len(obj), write)
294             write(LIST)
295             for elem in obj:
296                 self._encode(elem, write)
297         elif isinstance(obj, (int, long)):
298             if obj < self._smallestLongInt or obj > self._largestLongInt:
299                 raise BananaError(
300                     "int/long is too large to send (%d)" % (obj,))
301             if obj < self._smallestInt:
302                 int2b128(-obj, write)
303                 write(LONGNEG)
304             elif obj < 0:
305                 int2b128(-obj, write)
306                 write(NEG)
307             elif obj <= self._largestInt:
308                 int2b128(obj, write)
309                 write(INT)
310             else:
311                 int2b128(obj, write)
312                 write(LONGINT)
313         elif isinstance(obj, float):
314             write(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)
321                 write(VOCAB)
322             else:
323                 if len(obj) > SIZE_LIMIT:
324                     raise BananaError(
325                         "string is too long to send (%d)" % (len(obj),))
326                 int2b128(len(obj), write)
327                 write(STRING)
328                 write(obj)
329         else:
330             raise BananaError("could not send object: %r" % (obj,))
331
332
333 # For use from the interactive interpreter
334 _i = Banana()
335 _i.connectionMade()
336 _i._selectDialect("none")
337
338
339 def encode(lst):
340     """Encode a list s-expression."""
341     io = cStringIO.StringIO()
342     _i.transport = io
343     _i.sendEncoded(lst)
344     return io.getvalue()
345
346
347 def decode(st):
348     """
349     Decode a banana-encoded string.
350     """
351     l = []
352     _i.expressionReceived = l.append
353     try:
354         _i.dataReceived(st)
355     finally:
356         _i.buffer = ''
357         del _i.expressionReceived
358     return l[0]