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
577 """
578 Write string to the data stream.
579
580 @type s: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
581 @param s: The string data to be encoded to the AMF0 data stream.
582 @type writeType: C{bool}
583 @param writeType: Write data type.
584 """
585 t = type(s)
586
587 if t is str:
588 pass
589 elif isinstance(s, unicode):
590 s = s.encode('utf8')
591 elif not isinstance(s, basestring):
592 s = unicode(s).encode('utf8')
593
594 l = len(s)
595
596 if writeType:
597 if l > 0xffff:
598 self.writeType(TYPE_LONGSTRING)
599 else:
600 self.writeType(TYPE_STRING)
601
602 if l > 0xffff:
603 self.stream.write_ulong(l)
604 else:
605 self.stream.write_ushort(l)
606
607 self.stream.write(s)
608
610 """
611 Write reference to the data stream.
612
613 @type o: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
614 @param o: The reference data to be encoded to the AMF0 data
615 stream.
616 """
617 idx = self.context.getObjectReference(o)
618
619 if idx is None:
620 return None
621
622 self.writeType(TYPE_REFERENCE)
623 self.stream.write_ushort(idx)
624
625 return idx
626
628 """
629 Write C{dict} to the data stream.
630
631 @type o: C{iterable}
632 @param o: The C{dict} data to be encoded to the AMF0 data
633 stream.
634 """
635 for key, val in o.iteritems():
636 self.writeString(key, False)
637 self.writeElement(val)
638
640 """
641 Write mixed array to the data stream.
642
643 @type o: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
644 @param o: The mixed array data to be encoded to the AMF0
645 data stream.
646 """
647 if self.writeReference(o) is not None:
648 return
649
650 self.context.addObject(o)
651 self.writeType(TYPE_MIXEDARRAY)
652
653
654
655 try:
656
657 max_index = max([y[0] for y in o.items()
658 if isinstance(y[0], (int, long))])
659
660 if max_index < 0:
661 max_index = 0
662 except ValueError:
663 max_index = 0
664
665 self.stream.write_ulong(max_index)
666
667 self._writeDict(o)
668 self._writeEndObject()
669
673
718
720 """
721 Writes a date to the data stream.
722
723 @type d: Instance of C{datetime.datetime}
724 @param d: The date to be encoded to the AMF0 data stream.
725 """
726 if isinstance(d, datetime.time):
727 raise pyamf.EncodeError('A datetime.time instance was found but '
728 'AMF0 has no way to encode time objects. Please use '
729 'datetime.datetime instead (got:%r)' % (d,))
730
731
732
733 if self.timezone_offset is not None:
734 d -= self.timezone_offset
735
736 secs = util.get_timestamp(d)
737 tz = 0
738
739 self.writeType(TYPE_DATE)
740 self.stream.write_double(secs * 1000.0)
741 self.stream.write_short(tz)
742
744 """
745 Write XML to the data stream.
746
747 @type e: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
748 @param e: The XML data to be encoded to the AMF0 data stream.
749 """
750 if self.use_amf3 is True:
751 self.writeAMF3(e)
752
753 return
754
755 self.writeType(TYPE_XML)
756
757 data = util.ET.tostring(e, 'utf-8')
758 self.stream.write_ulong(len(data))
759 self.stream.write(data)
760
762 """
763 Writes an element to the datastream in L{AMF3<pyamf.amf3>} format.
764
765 @type data: C{mixed}
766 @param data: The data to be encoded to the AMF0 data stream.
767 """
768 if not hasattr(self.context, 'amf3_context'):
769 self.context.amf3_context = pyamf.get_context(pyamf.AMF3, exceptions=False)
770
771 if not hasattr(self.context, 'amf3_encoder'):
772 self.context.amf3_encoder = pyamf.get_encoder(
773 pyamf.AMF3, self.stream, self.context.amf3_context)
774
775 self.context.addAMF3Object(data)
776 encoder = self.context.amf3_encoder
777
778 self.writeType(TYPE_AMF3)
779 encoder.writeElement(data)
780
781
783 """
784 A helper function to decode an AMF0 datastream.
785 """
786 decoder = Decoder(*args, **kwargs)
787
788 while 1:
789 try:
790 yield decoder.readElement()
791 except pyamf.EOStream:
792 break
793
794
796 """
797 A helper function to encode an element into the AMF0 format.
798
799 @type element: C{mixed}
800 @keyword element: The element to encode
801 @type context: L{Context<pyamf.amf0.Context>}
802 @keyword context: AMF0 C{Context} to use for the encoding. This holds
803 previously referenced objects etc.
804 @rtype: C{StringIO}
805 @return: The encoded stream.
806 """
807 encoder = Encoder(**kwargs)
808
809 for element in args:
810 encoder.writeElement(element)
811
812 return encoder.stream
813
814
816 """
817 I represent the C{RecordSet} class used in Adobe Flash Remoting to hold
818 (amongst other things) SQL records.
819
820 @ivar columns: The columns to send.
821 @type columns: List of strings.
822 @ivar items: The C{RecordSet} data.
823 @type items: List of lists, the order of the data corresponds to the order
824 of the columns.
825 @ivar service: Service linked to the C{RecordSet}.
826 @type service:
827 @ivar id: The id of the C{RecordSet}.
828 @type id: C{str}
829
830 @see: U{RecordSet on OSFlash (external)
831 <http://osflash.org/documentation/amf/recordset>}
832 """
833
838
839 - def __init__(self, columns=[], items=[], service=None, id=None):
840 self.columns = columns
841 self.items = items
842 self.service = service
843 self.id = id
844
846 ret = pyamf.ASObject(totalCount=len(self.items), cursor=1, version=1,
847 initialData=self.items, columnNames=self.columns)
848
849 if self.service is not None:
850 ret.update({'serviceName': str(self.service['name'])})
851
852 if self.id is not None:
853 ret.update({'id':str(self.id)})
854
855 return ret
856
858 self.columns = val['columnNames']
859 self.items = val['initialData']
860
861 try:
862
863 self.service = dict(name=val['serviceName'])
864 except KeyError:
865 self.service = None
866
867 try:
868 self.id = val['id']
869 except KeyError:
870 self.id = None
871
872 serverInfo = property(_get_server_info, _set_server_info)
873
875 ret = '<%s.%s object' % (self.__module__, self.__class__.__name__)
876
877 if self.id is not None:
878 ret += ' id=%s' % self.id
879
880 if self.service is not None:
881 ret += ' service=%s' % self.service
882
883 ret += ' at 0x%x>' % id(self)
884
885 return ret
886
887 pyamf.register_class(RecordSet)
888
889
891 """
892 This is a compatibility function that takes a C{float} and converts it to an
893 C{int} if the values are equal.
894 """
895 try:
896 y = int(x)
897 except (OverflowError, ValueError):
898 pass
899 else:
900
901 if x == x and y == x:
902 return y
903
904 return x
905
906
907 try:
908 float('nan')
909 except ValueError:
910 pass
911 else:
912 if float('nan') == 0:
914 def f2(x):
915 if str(x).lower().find('nan') >= 0:
916 return x
917
918 return f2.func(x)
919 f2.func = func
920
921 return f2
922
923 _check_for_int = check_nan(_check_for_int)
924