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 Adobe 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
20 """
21
22 import datetime
23 import types
24 import copy
25
26 import pyamf
27 from pyamf import util
28
29
30
31
32 TYPE_NUMBER = '\x00'
33
34
35 TYPE_BOOL = '\x01'
36
37
38 TYPE_STRING = '\x02'
39
40
41 TYPE_OBJECT = '\x03'
42
43
44 TYPE_MOVIECLIP = '\x04'
45
46 TYPE_NULL = '\x05'
47
48 TYPE_UNDEFINED = '\x06'
49
50
51
52
53 TYPE_REFERENCE = '\x07'
54
55
56
57 TYPE_MIXEDARRAY = '\x08'
58
59 TYPE_OBJECTTERM = '\x09'
60
61
62
63 TYPE_ARRAY = '\x0A'
64
65
66
67
68
69 TYPE_DATE = '\x0B'
70
71
72 TYPE_LONGSTRING = '\x0C'
73
74
75 TYPE_UNSUPPORTED = '\x0D'
76
77
78
79
80 TYPE_RECORDSET = '\x0E'
81
82
83
84
85 TYPE_XML = '\x0F'
86
87
88
89
90 TYPE_TYPEDOBJECT = '\x10'
91
92
93
94
95 TYPE_AMF3 = '\x11'
96
97
98 -class Context(pyamf.BaseContext):
99 """
100 I hold the AMF0 context for en/decoding streams.
101
102 AMF0 object references start at index 1.
103
104 @ivar amf3_objs: A list of objects that have been decoded in
105 L{AMF3<pyamf.amf3>}.
106 @type amf3_objs: L{util.IndexedCollection}
107 """
108
109 - def __init__(self, **kwargs):
110 self.amf3_objs = []
111
112 pyamf.BaseContext.__init__(self, **kwargs)
113
115 """
116 Clears the context.
117 """
118 pyamf.BaseContext.clear(self)
119
120 self.amf3_objs = []
121
122 if hasattr(self, 'amf3_context'):
123 self.amf3_context.clear()
124
126 """
127 Gets a reference for an object.
128
129 @raise ReferenceError: Unknown AMF3 object reference.
130 """
131 return obj in self.amf3_objs
132 o = self.amf3_objs.getReferenceTo(obj)
133
134 if o is None and self.exceptions:
135 raise pyamf.ReferenceError(
136 'Unknown AMF3 reference for (%r)' % (obj,))
137
138 return o
139
140 - def addAMF3Object(self, obj):
141 """
142 Adds an AMF3 reference to C{obj}.
143
144 @type obj: C{mixed}
145 @param obj: The object to add to the context.
146 @rtype: C{int}
147 @return: Reference to C{obj}.
148 """
149 return self.amf3_objs.append(obj)
150
151 - def __copy__(self):
152 cpy = self.__class__(exceptions=self.exceptions)
153 cpy.amf3_objs = copy.copy(self.amf3_objs)
154
155 return cpy
156
157
159 """
160 Decodes an AMF0 stream.
161 """
162
163 context_class = Context
164
165
166 type_map = {
167 TYPE_NUMBER: 'readNumber',
168 TYPE_BOOL: 'readBoolean',
169 TYPE_STRING: 'readString',
170 TYPE_OBJECT: 'readObject',
171 TYPE_NULL: 'readNull',
172 TYPE_UNDEFINED: 'readUndefined',
173 TYPE_REFERENCE: 'readReference',
174 TYPE_MIXEDARRAY: 'readMixedArray',
175 TYPE_ARRAY: 'readList',
176 TYPE_DATE: 'readDate',
177 TYPE_LONGSTRING: 'readLongString',
178
179 TYPE_UNSUPPORTED:'readNull',
180 TYPE_XML: 'readXML',
181 TYPE_TYPEDOBJECT:'readTypedObject',
182 TYPE_AMF3: 'readAMF3'
183 }
184
186 """
187 Reads a ActionScript C{Number} value.
188
189 In ActionScript 1 and 2 the C{NumberASTypes} type represents all numbers,
190 both floats and integers.
191
192 @rtype: C{int} or C{float}
193 """
194 return _check_for_int(self.stream.read_double())
195
197 """
198 Reads a ActionScript C{Boolean} value.
199
200 @rtype: C{bool}
201 @return: Boolean.
202 """
203 return bool(self.stream.read_uchar())
204
206 """
207 Reads a ActionScript C{null} value.
208
209 @return: C{None}
210 @rtype: C{None}
211 """
212 return None
213
215 """
216 Reads an ActionScript C{undefined} value.
217
218 @return: L{Undefined<pyamf.Undefined>}
219 """
220 return pyamf.Undefined
221
223 """
224 Read mixed array.
225
226 @rtype: C{dict}
227 @return: C{dict} read from the stream
228 """
229 len = self.stream.read_ulong()
230 obj = pyamf.MixedArray()
231 self.context.addObject(obj)
232 self._readObject(obj)
233 ikeys = []
234
235 for key in obj.keys():
236 try:
237 ikey = int(key)
238 ikeys.append((key, ikey))
239 obj[ikey] = obj[key]
240 del obj[key]
241 except ValueError:
242
243 pass
244
245 ikeys.sort()
246
247 return obj
248
250 """
251 Read a C{list} from the data stream.
252
253 @rtype: C{list}
254 @return: C{list}
255 """
256 obj = []
257 self.context.addObject(obj)
258 len = self.stream.read_ulong()
259
260 for i in xrange(len):
261 obj.append(self.readElement())
262
263 return obj
264
289
291 """
292 Read AMF3 elements from the data stream.
293
294 @rtype: C{mixed}
295 @return: The AMF3 element read from the stream
296 """
297 if not hasattr(self.context, 'amf3_context'):
298 self.context.amf3_context = pyamf.get_context(pyamf.AMF3, exceptions=False)
299
300 if not hasattr(self.context, 'amf3_decoder'):
301 self.context.amf3_decoder = pyamf.get_decoder(
302 pyamf.AMF3, self.stream, self.context.amf3_context)
303
304 decoder = self.context.amf3_decoder
305 element = decoder.readElement()
306 self.context.addAMF3Object(element)
307
308 return element
309
311 """
312 Reads a string from the data stream.
313
314 @rtype: C{str}
315 @return: string
316 """
317 len = self.stream.read_ushort()
318 return self.stream.read_utf8_string(len)
319
336
338 """
339 Reads an object from the data stream.
340
341 @rtype: L{ASObject<pyamf.ASObject>}
342 """
343 obj = pyamf.ASObject()
344 self.context.addObject(obj)
345
346 self._readObject(obj)
347
348 return obj
349
351 """
352 Reads a reference from the data stream.
353
354 @raise pyamf.ReferenceError: Unknown reference.
355 """
356 idx = self.stream.read_ushort()
357
358 o = self.context.getObject(idx)
359
360 if o is None:
361 raise pyamf.ReferenceError('Unknown reference %d' % (idx,))
362
363 return o
364
366 """
367 Reads a UTC date from the data stream. Client and servers are
368 responsible for applying their own timezones.
369
370 Date: C{0x0B T7 T6} .. C{T0 Z1 Z2 T7} to C{T0} form a 64 bit
371 Big Endian number that specifies the number of nanoseconds
372 that have passed since 1/1/1970 0:00 to the specified time.
373 This format is UTC 1970. C{Z1} and C{Z0} for a 16 bit Big
374 Endian number indicating the indicated time's timezone in
375 minutes.
376 """
377 ms = self.stream.read_double() / 1000.0
378 tz = self.stream.read_short()
379
380
381 d = util.get_datetime(ms)
382
383 if self.timezone_offset:
384 d = d + self.timezone_offset
385
386 self.context.addObject(d)
387
388 return d
389
397
407
408
410 """
411 Encodes an AMF0 stream.
412
413 @ivar use_amf3: A flag to determine whether this encoder knows about AMF3.
414 @type use_amf3: C{bool}
415 """
416
417 context_class = Context
418
419 type_map = [
420 ((types.BuiltinFunctionType, types.BuiltinMethodType,
421 types.FunctionType, types.GeneratorType, types.ModuleType,
422 types.LambdaType, types.MethodType), "writeFunc"),
423 ((types.NoneType,), "writeNull"),
424 ((bool,), "writeBoolean"),
425 ((int,long,float), "writeNumber"),
426 ((types.StringTypes,), "writeString"),
427 ((pyamf.ASObject,), "writeObject"),
428 ((pyamf.MixedArray,), "writeMixedArray"),
429 ((types.ListType, types.TupleType,), "writeArray"),
430 ((datetime.date, datetime.datetime, datetime.time), "writeDate"),
431 ((util.is_ET_element,), "writeXML"),
432 ((lambda x: x is pyamf.Undefined,), "writeUndefined"),
433 ((types.ClassType, types.TypeType), "writeClass"),
434 ((types.InstanceType,types.ObjectType,), "writeObject"),
435 ]
436
441
443 """
444 Writes the type to the stream.
445
446 @type t: C{str}
447 @param t: ActionScript type.
448
449 @raise pyamf.EncodeError: AMF0 type is not recognized.
450 """
451 self.stream.write(t)
452
454 """
455 Writes the L{undefined<TYPE_UNDEFINED>} data type to the stream.
456
457 @param data: The C{undefined} data to be encoded to the AMF0 data
458 stream.
459 @type data: C{undefined} data
460 """
461 self.writeType(TYPE_UNDEFINED)
462
464 """
465 Classes cannot be serialised.
466 """
467 raise pyamf.EncodeError("Class objects cannot be serialised")
468
470 """
471 Functions cannot be serialised.
472 """
473 raise pyamf.EncodeError("Callables cannot be serialised")
474
476 """
477 Writes L{unsupported<TYPE_UNSUPPORTED>} data type to the
478 stream.
479
480 @param data: The C{unsupported} data to be encoded to the AMF0
481 data stream.
482 @type data: C{unsupported} data
483 """
484 self.writeType(TYPE_UNSUPPORTED)
485
500
502 """
503 Writes the data.
504
505 @type data: C{mixed}
506 @param data: The data to be encoded to the AMF0 data stream.
507 @raise EncodeError: Cannot find encoder func.
508 """
509 func = self._writeElementFunc(data)
510
511 if func is None:
512 raise pyamf.EncodeError("Cannot find encoder func for %r" % (data,))
513
514 func(data)
515
517 """
518 Write null type to data stream.
519
520 @type n: C{None}
521 @param n: Is ignored.
522 """
523 self.writeType(TYPE_NULL)
524
551
553 """
554 Write number to the data stream.
555
556 @type n: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
557 @param n: The number data to be encoded to the AMF0 data stream.
558 """
559 self.writeType(TYPE_NUMBER)
560 self.stream.write_double(float(n))
561
563 """
564 Write boolean to the data stream.
565
566 @type b: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
567 @param b: The boolean data to be encoded to the AMF0 data stream.
568 """
569 self.writeType(TYPE_BOOL)
570
571 if b:
572 self.stream.write_uchar(1)
573 else:
574 self.stream.write_uchar(0)
575
585
587 """
588 Write string to the data stream.
589
590 @type s: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
591 @param s: The string data to be encoded to the AMF0 data stream.
592 @type writeType: C{bool}
593 @param writeType: Write data type.
594 """
595 t = type(s)
596
597 if t is str:
598 pass
599 elif isinstance(s, unicode):
600 s = s.encode('utf8')
601 elif not isinstance(s, basestring):
602 s = unicode(s).encode('utf8')
603
604 if writeType:
605 if len(s) > 0xffff:
606 self.writeType(TYPE_LONGSTRING)
607 else:
608 self.writeType(TYPE_STRING)
609
610 self._writeString(s)
611
613 """
614 Write reference to the data stream.
615
616 @type o: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
617 @param o: The reference data to be encoded to the AMF0 data
618 stream.
619 """
620 idx = self.context.getObjectReference(o)
621
622 if idx is None:
623 return None
624
625 self.writeType(TYPE_REFERENCE)
626 self.stream.write_ushort(idx)
627
628 return idx
629
631 """
632 Write C{dict} to the data stream.
633
634 @type o: C{iterable}
635 @param o: The C{dict} data to be encoded to the AMF0 data
636 stream.
637 """
638 for key, val in o.iteritems():
639 self.writeString(key, False)
640 self.writeElement(val)
641
643 """
644 Write mixed array to the data stream.
645
646 @type o: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
647 @param o: The mixed array data to be encoded to the AMF0
648 data stream.
649 """
650 if self.writeReference(o) is not None:
651 return
652
653 self.context.addObject(o)
654 self.writeType(TYPE_MIXEDARRAY)
655
656
657
658 try:
659
660 max_index = max([y[0] for y in o.items()
661 if isinstance(y[0], (int, long))])
662
663 if max_index < 0:
664 max_index = 0
665 except ValueError:
666 max_index = 0
667
668 self.stream.write_ulong(max_index)
669
670 self._writeDict(o)
671 self._writeEndObject()
672
676
721
723 """
724 Writes a date to the data stream.
725
726 @type d: Instance of C{datetime.datetime}
727 @param d: The date to be encoded to the AMF0 data stream.
728 """
729 if isinstance(d, datetime.time):
730 raise pyamf.EncodeError('A datetime.time instance was found but '
731 'AMF0 has no way to encode time objects. Please use '
732 'datetime.datetime instead (got:%r)' % (d,))
733
734
735
736 if self.timezone_offset is not None:
737 d -= self.timezone_offset
738
739 secs = util.get_timestamp(d)
740 tz = 0
741
742 self.writeType(TYPE_DATE)
743 self.stream.write_double(secs * 1000.0)
744 self.stream.write_short(tz)
745
747 """
748 Write XML to the data stream.
749
750 @type e: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
751 @param e: The XML data to be encoded to the AMF0 data stream.
752 """
753 if self.use_amf3 is True:
754 self.writeAMF3(e)
755
756 return
757
758 self.writeType(TYPE_XML)
759
760 data = util.ET.tostring(e, 'utf-8')
761 self.stream.write_ulong(len(data))
762 self.stream.write(data)
763
765 """
766 Writes an element to the datastream in L{AMF3<pyamf.amf3>} format.
767
768 @type data: C{mixed}
769 @param data: The data to be encoded to the AMF0 data stream.
770 """
771 if not hasattr(self.context, 'amf3_context'):
772 self.context.amf3_context = pyamf.get_context(pyamf.AMF3, exceptions=False)
773
774 if not hasattr(self.context, 'amf3_encoder'):
775 self.context.amf3_encoder = pyamf.get_encoder(
776 pyamf.AMF3, self.stream, self.context.amf3_context)
777
778 self.context.addAMF3Object(data)
779 encoder = self.context.amf3_encoder
780
781 self.writeType(TYPE_AMF3)
782 encoder.writeElement(data)
783
784
786 """
787 A helper function to decode an AMF0 datastream.
788 """
789 decoder = Decoder(*args, **kwargs)
790
791 while 1:
792 try:
793 yield decoder.readElement()
794 except pyamf.EOStream:
795 break
796
797
799 """
800 A helper function to encode an element into the AMF0 format.
801
802 @type element: C{mixed}
803 @keyword element: The element to encode
804 @type context: L{Context<pyamf.amf0.Context>}
805 @keyword context: AMF0 C{Context} to use for the encoding. This holds
806 previously referenced objects etc.
807 @rtype: C{StringIO}
808 @return: The encoded stream.
809 """
810 encoder = Encoder(**kwargs)
811
812 for element in args:
813 encoder.writeElement(element)
814
815 return encoder.stream
816
817
819 """
820 I represent the C{RecordSet} class used in Adobe Flash Remoting to hold
821 (amongst other things) SQL records.
822
823 @ivar columns: The columns to send.
824 @type columns: List of strings.
825 @ivar items: The C{RecordSet} data.
826 @type items: List of lists, the order of the data corresponds to the order
827 of the columns.
828 @ivar service: Service linked to the C{RecordSet}.
829 @type service:
830 @ivar id: The id of the C{RecordSet}.
831 @type id: C{str}
832
833 @see: U{RecordSet on OSFlash (external)
834 <http://osflash.org/documentation/amf/recordset>}
835 """
836
841
842 - def __init__(self, columns=[], items=[], service=None, id=None):
843 self.columns = columns
844 self.items = items
845 self.service = service
846 self.id = id
847
849 ret = pyamf.ASObject(totalCount=len(self.items), cursor=1, version=1,
850 initialData=self.items, columnNames=self.columns)
851
852 if self.service is not None:
853 ret.update({'serviceName': str(self.service['name'])})
854
855 if self.id is not None:
856 ret.update({'id':str(self.id)})
857
858 return ret
859
861 self.columns = val['columnNames']
862 self.items = val['initialData']
863
864 try:
865
866 self.service = dict(name=val['serviceName'])
867 except KeyError:
868 self.service = None
869
870 try:
871 self.id = val['id']
872 except KeyError:
873 self.id = None
874
875 serverInfo = property(_get_server_info, _set_server_info)
876
878 ret = '<%s.%s object' % (self.__module__, self.__class__.__name__)
879
880 if self.id is not None:
881 ret += ' id=%s' % self.id
882
883 if self.service is not None:
884 ret += ' service=%s' % self.service
885
886 ret += ' at 0x%x>' % id(self)
887
888 return ret
889
890 pyamf.register_class(RecordSet)
891
892
894 """
895 This is a compatibility function that takes a C{float} and converts it to an
896 C{int} if the values are equal.
897 """
898 try:
899 y = int(x)
900 except (OverflowError, ValueError):
901 pass
902 else:
903
904 if x == x and y == x:
905 return y
906
907 return x
908
909
910 try:
911 float('nan')
912 except ValueError:
913 pass
914 else:
915 if float('nan') == 0:
917 def f2(x):
918 if str(x).lower().find('nan') >= 0:
919 return x
920
921 return f2.func(x)
922 f2.func = func
923
924 return f2
925
926 _check_for_int = check_nan(_check_for_int)
927