1
2
3
4
5
6 """
7 AMF0 implementation.
8
9 C{AMF0} supports the basic data types used for the NetConnection, NetStream,
10 LocalConnection, SharedObjects and other classes in the Flash Player.
11
12 @see: U{Official AMF0 Specification in English (external)
13 <http://opensource.adobe.com/wiki/download/attachments/1114283/amf0_spec_121207.pdf>}
14 @see: U{Official AMF0 Specification in Japanese (external)
15 <http://opensource.adobe.com/wiki/download/attachments/1114283/JP_amf0_spec_121207.pdf>}
16 @see: U{AMF documentation on OSFlash (external)
17 <http://osflash.org/documentation/amf>}
18
19 @author: U{Arnar Birgisson<mailto:arnarbi@gmail.com>}
20 @author: U{Thijs Triemstra<mailto:info@collab.nl>}
21 @author: U{Nick Joyce<mailto:nick@boxdesign.co.uk>}
22
23 @since: 0.1.0
24 """
25
26 import datetime, types
27
28 import pyamf
29 from pyamf import util
30
104
105
106 ACTIONSCRIPT_TYPES = []
107
108 for x in ASTypes.__dict__:
109 if not x.startswith('_'):
110 ACTIONSCRIPT_TYPES.append(ASTypes.__dict__[x])
111 del x
112
113 -class Context(pyamf.BaseContext):
114 """
115 I hold the AMF0 context for en/decoding streams.
116
117 AMF0 object references start at index 1.
118 """
120 """
121 Resets the context.
122
123 The C{amf3_objs} var keeps a list of objects that were encoded
124 in L{AMF3<pyamf.amf3>}.
125 """
126 pyamf.BaseContext.clear(self)
127
128 self.amf3_objs = []
129 self.rev_amf3_objs = {}
130
131 if hasattr(self, 'amf3_context'):
132 self.amf3_context.clear()
133
134 - def _getObject(self, ref):
135 return self.objects[ref + 1]
136
137 - def __copy__(self):
138 copy = self.__class__()
139 copy.amf3_objs = self.amf3_objs
140 copy.rev_amf3_objs = self.rev_amf3_objs
141
142 return copy
143
145 """
146 Gets a reference for an object.
147
148 @raise ReferenceError: Object reference could not be found.
149 """
150 try:
151 return self.rev_amf3_objs[id(obj)]
152 except KeyError:
153 raise ReferenceError
154
155 - def addAMF3Object(self, obj):
156 """
157 Adds an AMF3 reference to C{obj}.
158
159 @type obj: C{mixed}
160 @param obj: The object to add to the context.
161
162 @rtype: C{int}
163 @return: Reference to C{obj}.
164 """
165 self.amf3_objs.append(obj)
166 idx = len(self.amf3_objs) - 1
167 self.rev_amf3_objs[id(obj)] = idx
168
169 return idx
170
172 """
173 Decodes an AMF0 stream.
174 """
175 context_class = Context
176
177
178 type_map = {
179 ASTypes.NUMBER: 'readNumber',
180 ASTypes.BOOL: 'readBoolean',
181 ASTypes.STRING: 'readString',
182 ASTypes.OBJECT: 'readObject',
183 ASTypes.NULL: 'readNull',
184 ASTypes.UNDEFINED: 'readUndefined',
185 ASTypes.REFERENCE: 'readReference',
186 ASTypes.MIXEDARRAY: 'readMixedArray',
187 ASTypes.ARRAY: 'readList',
188 ASTypes.DATE: 'readDate',
189 ASTypes.LONGSTRING: 'readLongString',
190
191 ASTypes.UNSUPPORTED:'readNull',
192 ASTypes.XML: 'readXML',
193 ASTypes.TYPEDOBJECT:'readTypedObject',
194 ASTypes.AMF3: 'readAMF3'
195 }
196
198 """
199 Read and returns the next byte in the stream and determine its type.
200
201 @raise DecodeError: AMF0 type not recognized.
202 @return: AMF0 type.
203 """
204 type = self.stream.read_uchar()
205
206 if type not in ACTIONSCRIPT_TYPES:
207 raise pyamf.DecodeError("Unknown AMF0 type 0x%02x at %d" % (
208 type, self.stream.tell() - 1))
209
210 return type
211
213 """
214 Reads a ActionScript C{Number} value.
215
216 In ActionScript 1 and 2 the C{NumberASTypes} type represents all numbers,
217 both floats and integers.
218
219 @rtype: C{int} or C{float}
220 """
221 return _check_for_int(self.stream.read_double())
222
224 """
225 Reads a ActionScript C{Boolean} value.
226
227 @rtype: C{bool}
228 @return: Boolean.
229 """
230 return bool(self.stream.read_uchar())
231
233 """
234 Reads a ActionScript C{null} value.
235
236 @return: C{None}
237 @rtype: C{None}
238 """
239 return None
240
242 """
243 Reads an ActionScript C{undefined} value.
244
245 @return: L{Undefined<pyamf.Undefined>}
246 """
247 return pyamf.Undefined
248
250 """
251 Read mixed array.
252
253 @rtype: C{dict}
254 @return: C{dict} read from the stream
255 """
256 len = self.stream.read_ulong()
257 obj = pyamf.MixedArray()
258 self.context.addObject(obj)
259 self._readObject(obj)
260 ikeys = []
261
262 for key in obj.keys():
263 try:
264 ikey = int(key)
265 ikeys.append((key, ikey))
266 obj[ikey] = obj[key]
267 del obj[key]
268 except ValueError:
269
270 pass
271
272 ikeys.sort()
273
274 return obj
275
277 """
278 Read a C{list} from the data stream.
279
280 @rtype: C{list}
281 @return: C{list}
282 """
283 obj = []
284 self.context.addObject(obj)
285 len = self.stream.read_ulong()
286
287 for i in xrange(len):
288 obj.append(self.readElement())
289
290 return obj
291
293 """
294 Reads an ActionScript object from the stream and attempts to
295 'cast' it.
296
297 @see: L{load_class<pyamf.load_class>}
298 """
299 classname = self.readString()
300 alias = pyamf.load_class(classname)
301
302 ret = alias()
303 self.context.addObject(ret)
304 self._readObject(ret, alias)
305
306 return ret
307
309 """
310 Read AMF3 elements from the data stream.
311
312 @rtype: C{mixed}
313 @return: The AMF3 element read from the stream
314 """
315 if not hasattr(self.context, 'amf3_context'):
316 from pyamf import amf3
317
318 self.context.amf3_context = amf3.Context()
319
320 decoder = pyamf._get_decoder_class(pyamf.AMF3)(self.stream, self.context.amf3_context)
321
322 element = decoder.readElement()
323 self.context.addAMF3Object(element)
324
325 return element
326
328 """
329 Reads a string from the data stream.
330
331 @rtype: C{str}
332 @return: string
333 """
334 len = self.stream.read_ushort()
335 return self.stream.read_utf8_string(len)
336
338 attrs = []
339
340 if alias is not None:
341 attrs = alias.getAttrs(obj)
342
343 key = self.readString()
344
345 ot = chr(ASTypes.OBJECTTERM)
346 obj_attrs = dict()
347
348 while self.stream.peek() != ot:
349 obj_attrs[key] = self.readElement()
350 key = self.readString()
351
352
353 self.stream.read(len(ot))
354
355 if attrs is None:
356 attrs = obj_attrs.keys()
357
358 if alias:
359 if hasattr(obj, '__setstate__'):
360 obj.__setstate__(obj_attrs)
361
362 return
363
364 for key in filter(lambda x: x in attrs, obj_attrs.keys()):
365 setattr(obj, key, obj_attrs[key])
366 else:
367 f = obj.__setattr__
368
369 if isinstance(obj, (list, dict)):
370 f = obj.__setitem__
371
372 for key, value in obj_attrs.iteritems():
373 f(key, value)
374
375 return
376
378 """
379 Reads an object from the data stream.
380
381 @rtype: L{ASObject<pyamf.ASObject>}
382 """
383 obj = pyamf.ASObject()
384 self.context.addObject(obj)
385
386 self._readObject(obj)
387
388 return obj
389
391 """
392 Reads a reference from the data stream.
393 """
394 idx = self.stream.read_ushort()
395
396 return self.context.getObject(idx)
397
399 """
400 Reads a UTC date from the data stream. Client and servers are
401 responsible for applying their own timezones.
402
403 Date: C{0x0B T7 T6} .. C{T0 Z1 Z2 T7} to C{T0} form a 64 bit
404 Big Endian number that specifies the number of nanoseconds
405 that have passed since 1/1/1970 0:00 to the specified time.
406 This format is UTC 1970. C{Z1} and C{Z0} for a 16 bit Big
407 Endian number indicating the indicated time's timezone in
408 minutes.
409 """
410 ms = self.stream.read_double() / 1000.0
411 tz = self.stream.read_short()
412
413
414 d = datetime.datetime.utcfromtimestamp(ms)
415 self.context.addObject(d)
416
417 return d
418
426
436
438 """
439 Encodes an AMF0 stream.
440 """
441 context_class = Context
442
443 type_map = [
444 ((types.BuiltinFunctionType, types.BuiltinMethodType,),
445 "writeUnsupported"),
446 ((types.NoneType,), "writeNull"),
447 ((bool,), "writeBoolean"),
448 ((int,long,float), "writeNumber"),
449 ((types.StringTypes,), "writeString"),
450 ((pyamf.has_alias,pyamf.ASObject), "writeObject"),
451 ((pyamf.MixedArray,), "writeMixedArray"),
452 ((types.ListType, types.TupleType,), "writeArray"),
453 ((datetime.date, datetime.datetime), "writeDate"),
454 ((util.ET.iselement,), "writeXML"),
455 ((lambda x: x is pyamf.Undefined,), "writeUndefined"),
456 ((types.InstanceType,types.ObjectType,), "writeObject"),
457 ]
458
460 """
461 Writes the type to the stream.
462
463 @type type: C{int}
464 @param type: ActionScript type.
465
466 @raise pyamf.EncodeError: AMF0 type is not recognized.
467 """
468 if type not in ACTIONSCRIPT_TYPES:
469 raise pyamf.EncodeError("Unknown AMF0 type 0x%02x at %d" % (
470 type, self.stream.tell() - 1))
471
472 self.stream.write_uchar(type)
473