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 @since: 0.1.0
20 """
21
22 import datetime, types
23 import copy
24
25 import pyamf
26 from pyamf import util
27
101
102
103 ACTIONSCRIPT_TYPES = []
104
105 for x in ASTypes.__dict__:
106 if not x.startswith('_'):
107 ACTIONSCRIPT_TYPES.append(ASTypes.__dict__[x])
108 del x
109
110 -class Context(pyamf.BaseContext):
111 """
112 I hold the AMF0 context for en/decoding streams.
113
114 AMF0 object references start at index 1.
115
116 @ivar amf3_objs: A list of objects that have been decoded in
117 L{AMF3<pyamf.amf3>}.
118 @type amf3_objs: L{util.IndexedCollection}
119 """
120 - def __init__(self):
124
126 """
127 Clears the context.
128 """
129 pyamf.BaseContext.clear(self)
130
131 self.amf3_objs.clear()
132
133 if hasattr(self, 'amf3_context'):
134 self.amf3_context.clear()
135
137 """
138 Resets the context.
139
140 @see: L{pyamf.BaseContext.reset}
141 """
142 pyamf.BaseContext.reset(self)
143
144 if hasattr(self, 'amf3_context'):
145 self.amf3_context.reset()
146
148 """
149 Gets a reference for an object.
150
151 @raise ReferenceError: Object reference could not be found.
152 """
153 try:
154 return self.amf3_objs.getReferenceTo(obj)
155 except KeyError:
156 raise ReferenceError
157
158 - def addAMF3Object(self, obj):
159 """
160 Adds an AMF3 reference to C{obj}.
161
162 @type obj: C{mixed}
163 @param obj: The object to add to the context.
164 @rtype: C{int}
165 @return: Reference to C{obj}.
166 """
167 return self.amf3_objs.append(obj)
168
169 - def __copy__(self):
170 cpy = self.__class__()
171 cpy.amf3_objs = copy.copy(self.amf3_objs)
172
173 return cpy
174
176 """
177 Decodes an AMF0 stream.
178 """
179
180 context_class = Context
181
182
183 type_map = {
184 ASTypes.NUMBER: 'readNumber',
185 ASTypes.BOOL: 'readBoolean',
186 ASTypes.STRING: 'readString',
187 ASTypes.OBJECT: 'readObject',
188 ASTypes.NULL: 'readNull',
189 ASTypes.UNDEFINED: 'readUndefined',
190 ASTypes.REFERENCE: 'readReference',
191 ASTypes.MIXEDARRAY: 'readMixedArray',
192 ASTypes.ARRAY: 'readList',
193 ASTypes.DATE: 'readDate',
194 ASTypes.LONGSTRING: 'readLongString',
195
196 ASTypes.UNSUPPORTED:'readNull',
197 ASTypes.XML: 'readXML',
198 ASTypes.TYPEDOBJECT:'readTypedObject',
199 ASTypes.AMF3: 'readAMF3'
200 }
201
203 """
204 Read and returns the next byte in the stream and determine its type.
205
206 @raise DecodeError: AMF0 type not recognized.
207 @return: AMF0 type.
208 """
209 type = self.stream.read_uchar()
210
211 if type not in ACTIONSCRIPT_TYPES:
212 raise pyamf.DecodeError("Unknown AMF0 type 0x%02x at %d" % (
213 type, self.stream.tell() - 1))
214
215 return type
216
218 """
219 Reads a ActionScript C{Number} value.
220
221 In ActionScript 1 and 2 the C{NumberASTypes} type represents all numbers,
222 both floats and integers.
223
224 @rtype: C{int} or C{float}
225 """
226 return _check_for_int(self.stream.read_double())
227
229 """
230 Reads a ActionScript C{Boolean} value.
231
232 @rtype: C{bool}
233 @return: Boolean.
234 """
235 return bool(self.stream.read_uchar())
236
238 """
239 Reads a ActionScript C{null} value.
240
241 @return: C{None}
242 @rtype: C{None}
243 """
244 return None
245
247 """
248 Reads an ActionScript C{undefined} value.
249
250 @return: L{Undefined<pyamf.Undefined>}
251 """
252 return pyamf.Undefined
253
255 """
256 Read mixed array.
257
258 @rtype: C{dict}
259 @return: C{dict} read from the stream
260 """
261 len = self.stream.read_ulong()
262 obj = pyamf.MixedArray()
263 self.context.addObject(obj)
264 self._readObject(obj)
265 ikeys = []
266
267 for key in obj.keys():
268 try:
269 ikey = int(key)
270 ikeys.append((key, ikey))
271 obj[ikey] = obj[key]
272 del obj[key]
273 except ValueError:
274
275 pass
276
277 ikeys.sort()
278
279 return obj
280
282 """
283 Read a C{list} from the data stream.
284
285 @rtype: C{list}
286 @return: C{list}
287 """
288 obj = []
289 self.context.addObject(obj)
290 len = self.stream.read_ulong()
291
292 for i in xrange(len):
293 obj.append(self.readElement())
294
295 return obj
296
298 """
299 Reads an ActionScript object from the stream and attempts to
300 'cast' it.
301
302 @see: L{load_class<pyamf.load_class>}
303 """
304 classname = self.readString()
305 alias = None
306
307 try:
308 alias = pyamf.load_class(classname)
309
310 ret = alias.createInstance(codec=self)
311 except pyamf.UnknownClassAlias:
312 if self.strict:
313 raise
314
315 ret = pyamf.TypedObject(classname)
316
317 self.context.addObject(ret)
318 self._readObject(ret, alias)
319
320 return ret
321
323 """
324 Read AMF3 elements from the data stream.
325
326 @rtype: C{mixed}
327 @return: The AMF3 element read from the stream
328 """
329 if not hasattr(self.context, 'amf3_context'):
330 from pyamf import amf3
331
332 self.context.amf3_context = amf3.Context()
333
334 decoder = pyamf._get_decoder_class(pyamf.AMF3)(self.stream, self.context.amf3_context, strict=self.strict)
335
336 element = decoder.readElement()
337 self.context.addAMF3Object(element)
338
339 return element
340
342 """
343 Reads a string from the data stream.
344
345 @rtype: C{str}
346 @return: string
347 """
348 len = self.stream.read_ushort()
349 return self.stream.read_utf8_string(len)
350
368
370 """
371 Reads an object from the data stream.
372
373 @rtype: L{ASObject<pyamf.ASObject>}
374 """
375 obj = pyamf.ASObject()
376 self.context.addObject(obj)
377
378 self._readObject(obj)
379
380 return obj
381
383 """
384 Reads a reference from the data stream.
385 """
386 idx = self.stream.read_ushort()
387
388 return self.context.getObject(idx)
389
391 """
392 Reads a UTC date from the data stream. Client and servers are
393 responsible for applying their own timezones.
394
395 Date: C{0x0B T7 T6} .. C{T0 Z1 Z2 T7} to C{T0} form a 64 bit
396 Big Endian number that specifies the number of nanoseconds
397 that have passed since 1/1/1970 0:00 to the specified time.
398 This format is UTC 1970. C{Z1} and C{Z0} for a 16 bit Big
399 Endian number indicating the indicated time's timezone in
400 minutes.
401 """
402 ms = self.stream.read_double() / 1000.0
403 tz = self.stream.read_short()
404
405
406 d = util.get_datetime(ms)
407 self.context.addObject(d)
408
409 return d
410
418
428
430 """
431 Encodes an AMF0 stream.
432
433 @ivar use_amf3: A flag to determine whether this encoder knows about AMF3.
434 @type use_amf3: C{bool}
435 """
436
437 context_class = Context
438
439 type_map = [
440 ((types.BuiltinFunctionType, types.BuiltinMethodType,
441 types.FunctionType, types.GeneratorType, types.ModuleType,
442 types.LambdaType, types.MethodType), "writeFunc"),
443 ((types.NoneType,), "writeNull"),
444 ((bool,), "writeBoolean"),
445 ((int,long,float), "writeNumber"),
446 ((types.StringTypes,), "writeString"),
447 ((pyamf.ASObject,), "writeObject"),
448 ((pyamf.MixedArray,), "writeMixedArray"),
449 ((types.ListType, types.TupleType,), "writeArray"),
450 ((datetime.date, datetime.datetime), "writeDate"),
451 ((util.is_ET_element,), "writeXML"),
452 ((lambda x: x is pyamf.Undefined,), "writeUndefined"),
453 ((types.InstanceType,types.ObjectType,), "writeObject"),
454 ]
455
460
462 """
463 Writes the type to the stream.
464
465 @type type: C{int}
466 @param type: ActionScript type.
467
468 @raise pyamf.EncodeError: AMF0 type is not recognized.
469 """
470 if type not in ACTIONSCRIPT_TYPES:
471 raise pyamf.EncodeError("Unknown AMF0 type 0x%02x at %d" % (
472 type, self.stream.tell() - 1))
473
474 self.stream.write_uchar(type)
475
477 """
478 Writes the L{undefined<ASTypes.UNDEFINED>} data type to the stream.
479
480 @param data: The C{undefined} data to be encoded to the AMF0 data
481 stream.
482 @type data: C{undefined} data
483 """
484 self.writeType(ASTypes.UNDEFINED)
485
487 """
488 Functions cannot be serialised.
489 """
490 raise pyamf.EncodeError("Callables cannot be serialised")
491
493 """
494 Writes L{unsupported<ASTypes.UNSUPPORTED>} data type to the
495 stream.
496
497 @param data: The C{unsupported} data to be encoded to the AMF0
498 data stream.
499 @type data: C{unsupported} data
500 """
501 self.writeType(ASTypes.UNSUPPORTED)
502
519
521 """
522 Writes the data.
523
524 @type data: C{mixed}
525 @param data: The data to be encoded to the AMF0 data stream.
526 @raise EncodeError: Cannot find encoder func.
527 """
528 func = self._writeElementFunc(data)
529
530 if func is None:
531 raise pyamf.EncodeError("Cannot find encoder func for %r" % (data,))
532 else:
533 try:
534 func(data)
535 except (KeyboardInterrupt, SystemExit):
536 raise
537 except pyamf.EncodeError:
538 raise
539 except:
540
541 raise
542
544 """
545 Write null type to data stream.
546
547 @type n: C{None}
548 @param n: Is ignored.
549 """
550 self.writeType(ASTypes.NULL)
551
579
581 """
582 Write number to the data stream.
583
584 @type n: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
585 @param n: The number data to be encoded to the AMF0 data stream.
586 """
587 self.writeType(ASTypes.NUMBER)
588 self.stream.write_double(float(n))
589
591 """
592 Write boolean to the data stream.
593
594 @type b: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
595 @param b: The boolean data to be encoded to the AMF0 data stream.
596 """
597 self.writeType(ASTypes.BOOL)
598
599 if b:
600 self.stream.write_uchar(1)
601 else:
602 self.stream.write_uchar(0)
603
605 if not isinstance(s, basestring):
606 s = unicode(s).encode('utf8')
607
608 if len(s) > 0xffff:
609 self.stream.write_ulong(len(s))
610 else:
611 self.stream.write_ushort(len(s))
612
613 self.stream.write(s)
614
616 """
617 Write string to the data stream.
618
619 @type s: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
620 @param s: The string data to be encoded to the AMF0 data stream.
621 @type writeType: C{bool}
622 @param writeType: Write data type.
623 """
624 if isinstance(s, unicode):
625 s = s.encode('utf8')
626 elif not isinstance(s, basestring):
627 s = unicode(s).encode('utf8')
628
629 if len(s) > 0xffff:
630 if writeType:
631 self.writeType(ASTypes.LONGSTRING)
632 else:
633 if writeType:
634 self.stream.write_uchar(ASTypes.STRING)
635
636 self._writeString(s)
637
639 """
640 Write reference to the data stream.
641
642 @type o: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
643 @param o: The reference data to be encoded to the AMF0 data
644 stream.
645 """
646 idx = self.context.getObjectReference(o)
647
648 self.writeType(ASTypes.REFERENCE)
649 self.stream.write_ushort(idx)
650
652 """
653 Write C{dict} to the data stream.
654
655 @type o: C{iterable}
656 @param o: The C{dict} data to be encoded to the AMF0 data
657 stream.
658 """
659 for key, val in o.iteritems():
660 self.writeString(key, False)
661 self.writeElement(val)
662
664 """
665 Write mixed array to the data stream.
666
667 @type o: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
668 @param o: The mixed array data to be encoded to the AMF0
669 data stream.
670 """
671 try:
672 self.writeReference(o)
673 return
674 except pyamf.ReferenceError:
675 self.context.addObject(o)
676
677 self.writeType(ASTypes.MIXEDARRAY)
678
679
680
681 try:
682
683 max_index = max([y[0] for y in o.items()
684 if isinstance(y[0], (int, long))])
685
686 if max_index < 0:
687 max_index = 0
688 except ValueError:
689 max_index = 0
690
691 self.stream.write_ulong(max_index)
692
693 self._writeDict(o)
694 self._writeEndObject()
695
701
703 """
704 @raise pyamf.EncodeError: Unable to determine object attributes.
705 """
706 obj_attrs = None
707
708 if alias is not None:
709 obj_attrs = {}
710
711 for attrs in alias.getAttributes(o, codec=self):
712 obj_attrs.update(attrs)
713
714 if obj_attrs is None:
715 obj_attrs = util.get_attrs(o)
716
717 if obj_attrs is None:
718 raise pyamf.EncodeError('Unable to determine object attributes')
719
720 return obj_attrs
721
763
765 """
766 Writes a date to the data stream.
767
768 @type d: Instance of C{datetime.datetime}
769 @param d: The date to be encoded to the AMF0 data stream.
770 """
771
772
773 secs = util.get_timestamp(d)
774 tz = 0
775
776 self.writeType(ASTypes.DATE)
777 self.stream.write_double(secs * 1000.0)
778 self.stream.write_short(tz)
779
781 """
782 Write XML to the data stream.
783
784 @type e: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
785 @param e: The XML data to be encoded to the AMF0 data stream.
786 """
787 if self.use_amf3 is True:
788 self.writeAMF3(e)
789
790 return
791
792 self.writeType(ASTypes.XML)
793
794 data = util.ET.tostring(e, 'utf-8')
795 self.stream.write_ulong(len(data))
796 self.stream.write(data)
797
815
816 -def decode(stream, context=None, strict=False):
817 """
818 A helper function to decode an AMF0 datastream.
819
820 @type stream: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
821 @param stream: AMF0 datastream.
822 @type context: L{Context<pyamf.amf0.Context>}
823 @param context: AMF0 Context.
824 """
825 decoder = Decoder(stream, context, strict=strict)
826
827 while 1:
828 try:
829 yield decoder.readElement()
830 except pyamf.EOStream:
831 break
832
834 """
835 A helper function to encode an element into the AMF0 format.
836
837 @type element: C{mixed}
838 @keyword element: The element to encode
839 @type context: L{Context<pyamf.amf0.Context>}
840 @keyword context: AMF0 C{Context} to use for the encoding. This holds
841 previously referenced objects etc.
842 @rtype: C{StringIO}
843 @return: The encoded stream.
844 """
845 context = kwargs.get('context', None)
846 buf = util.BufferedByteStream()
847 encoder = Encoder(buf, context)
848
849 for element in args:
850 encoder.writeElement(element)
851
852 return buf
853
855 """
856 I represent the C{RecordSet} class used in Flash Remoting to hold
857 (amongst other things) SQL records.
858
859 @ivar columns: The columns to send.
860 @type columns: List of strings.
861 @ivar items: The C{RecordSet} data.
862 @type items: List of lists, the order of the data corresponds to the order
863 of the columns.
864 @ivar service: Service linked to the C{RecordSet}.
865 @type service:
866 @ivar id: The id of the C{RecordSet}.
867 @type id: C{str}
868
869 @see: U{RecordSet on OSFlash (external)
870 <http://osflash.org/documentation/amf/recordset>}
871 """
872
873 - def __init__(self, columns=[], items=[], service=None, id=None):
874 self.columns = columns
875 self.items = items
876 self.service = service
877 self.id = id
878
880 ret = pyamf.ASObject(totalCount=len(self.items), cursor=1, version=1,
881 initialData=self.items, columnNames=self.columns)
882
883 if self.service is not None:
884 ret.update({'serviceName': str(self.service['name'])})
885
886 if self.id is not None:
887 ret.update({'id':str(self.id)})
888
889 return ret
890
892 self.columns = val['columnNames']
893 self.items = val['initialData']
894
895 try:
896
897 self.service = dict(name=val['serviceName'])
898 except KeyError:
899 self.service = None
900
901 try:
902 self.id = val['id']
903 except KeyError:
904 self.id = None
905
906 serverInfo = property(_get_server_info, _set_server_info)
907
909 ret = '<%s.%s object' % (self.__module__, self.__class__.__name__)
910
911 if self.id is not None:
912 ret += ' id=%s' % self.id
913
914 if self.service is not None:
915 ret += ' service=%s' % self.service
916
917 ret += ' at 0x%x>' % id(self)
918
919 return ret
920
921 pyamf.register_class(RecordSet, 'RecordSet', attrs=['serverInfo'], metadata=['amf0'])
922
924 """
925 This is a compatibility function that takes a C{float} and converts it to an
926 C{int} if the values are equal.
927 """
928 try:
929 y = int(x)
930 except (OverflowError, ValueError):
931 pass
932 else:
933
934 if x == x and y == x:
935 return y
936
937 return x
938
939
940 try:
941 float('nan')
942 except ValueError:
943 pass
944 else:
945 if float('nan') == 0:
947 def f2(x):
948 if str(x).lower().find('nan') >= 0:
949 return x
950
951 return f2.func(x)
952 f2.func = func
953
954 return f2
955
956 _check_for_int = check_nan(_check_for_int)
957