1
2
3
4
5
6 """
7 AMF3 implementation.
8
9 C{AMF3} is the default serialization for
10 U{ActionScript<http://en.wikipedia.org/wiki/ActionScript>} 3.0 and provides
11 various advantages over L{AMF0<pyamf.amf0>}, which is used for ActionScript 1.0
12 and 2.0. It adds support for sending C{int} and C{uint} objects as integers and
13 supports data types that are available only in ActionScript 3.0, such as
14 L{ByteArray} and L{ArrayCollection}.
15
16 @see: U{Official AMF3 Specification in English (external)
17 <http://opensource.adobe.com/wiki/download/attachments/1114283/amf3_spec_121207.pdf>}
18 @see: U{Official AMF3 Specification in Japanese (external)
19 <http://opensource.adobe.com/wiki/download/attachments/1114283/JP_amf3_spec_121207.pdf>}
20 @see: U{AMF3 documentation on OSFlash (external)
21 <http://osflash.org/documentation/amf3>}
22
23 @since: 0.1.0
24 """
25
26 import types, datetime, zlib
27
28 import pyamf
29 from pyamf import util
30 from pyamf.flex import ObjectProxy, ArrayCollection
31
32
33
34 use_proxies_default = False
35
36 try:
37 set()
38 except NameError:
39 from sets import Set as set
40
42 """
43 All AMF3 data types used in ActionScript 3.0.
44
45 AMF represents ActionScript objects by a single byte representing type, and
46 then by a type-specific byte array that may be of fixed length, may contain
47 length information, or may come with its own end code.
48
49 @see: U{AMF3 data types on OSFlash (external)
50 <http://osflash.org/documentation/amf3#data_types>}
51 """
52
53
54 UNDEFINED = 0x00
55
56
57 NULL = 0x01
58
59
60
61
62
63 BOOL_FALSE = 0x02
64
65
66
67
68
69 BOOL_TRUE = 0x03
70
71
72
73
74 INTEGER = 0x04
75
76
77
78
79
80
81
82 NUMBER = 0x05
83
84
85
86
87
88
89
90 STRING = 0x06
91
92
93
94
95
96
97
98
99
100
101 XML = 0x07
102
103
104
105 DATE = 0x08
106
107
108 ARRAY = 0x09
109
110 OBJECT = 0x0a
111
112
113
114
115
116 XMLSTRING = 0x0b
117
118
119
120
121
122
123 BYTEARRAY = 0x0c
124
125
126 ACTIONSCRIPT_TYPES = []
127
128 for x in ASTypes.__dict__:
129 if not x.startswith('_'):
130 ACTIONSCRIPT_TYPES.append(ASTypes.__dict__[x])
131 del x
132
133
134 REFERENCE_BIT = 0x01
135
137 """
138 AMF object encodings.
139 """
140
141
142
143
144 STATIC = 0x00
145
146
147
148
149
150 EXTERNAL = 0x01
151
152
153
154
155
156
157 DYNAMIC = 0x02
158
159
160 PROXY = 0x03
161
163 """
164 I am a C{StringIO} type object containing byte data from the AMF stream.
165 ActionScript 3.0 introduced the C{flash.utils.ByteArray} class to support
166 the manipulation of raw data in the form of an Array of bytes.
167 I provide a set of methods for writing binary data with ActionScript 3.0.
168
169 This class is the I/O counterpart to the L{DataInput} class, which reads
170 binary data.
171
172 @see: U{IDataOutput on Livedocs (external)
173 <http://livedocs.adobe.com/flex/201/langref/flash/utils/IDataOutput.html>}
174 """
176 """
177 @param encoder: Encoder containing the stream.
178 @type encoder: L{amf3.Encoder<pyamf.amf3.Encoder>}
179 """
180 assert isinstance(encoder, Encoder)
181
182 self.encoder = encoder
183 self.stream = encoder.stream
184
186 """
187 Writes a Boolean value.
188
189 @type value: C{bool}
190 @param value: A C{Boolean} value determining which byte is written.
191 If the parameter is C{True}, C{1} is written; if C{False}, C{0} is
192 written.
193
194 @raise ValueError: Non-boolean value found.
195 """
196 if isinstance(value, bool):
197 if value is True:
198 self.stream.write_uchar(1)
199 else:
200 self.stream.write_uchar(0)
201 else:
202 raise ValueError("Non-boolean value found")
203
205 """
206 Writes a byte.
207
208 @type value: C{int}
209 """
210 self.stream.write_char(value)
211
213 """
214 Writes an IEEE 754 double-precision (64-bit) floating
215 point number.
216
217 @type value: C{number}
218 """
219 self.stream.write_double(value)
220
222 """
223 Writes an IEEE 754 single-precision (32-bit) floating
224 point number.
225
226 @type value: C{float}
227 """
228 self.stream.write_float(value)
229
231 """
232 Writes a 32-bit signed integer.
233
234 @type value: C{int}
235 """
236 self.stream.write_long(value)
237
239 """
240 Writes a multibyte string to the datastream using the
241 specified character set.
242
243 @type value: C{str}
244 @param value: The string value to be written.
245 @type charset: C{str}
246 @param charset: The string denoting the character
247 set to use. Possible character set strings include
248 C{shift-jis}, C{cn-gb}, C{iso-8859-1} and others.
249 @see: U{Supported character sets on Livedocs (external)
250 <http://livedocs.adobe.com/flex/201/langref/charset-codes.html>}
251 """
252 self.stream.write(unicode(value).encode(charset))
253
255 """
256 Writes an object to data stream in AMF serialized format.
257
258 @param value: The object to be serialized.
259 @type use_references: C{bool}
260 @param use_references:
261 """
262 self.encoder.writeElement(value, use_references)
263
265 """
266 Writes a 16-bit integer.
267
268 @type value: C{int}
269 @param value: A byte value as an integer.
270 """
271 self.stream.write_short(value)
272
274 """
275 Writes a 32-bit unsigned integer.
276
277 @type value: C{int}
278 @param value: A byte value as an unsigned integer.
279 """
280 self.stream.write_ulong(value)
281
283 """
284 Writes a UTF-8 string to the data stream.
285
286 The length of the UTF-8 string in bytes is written first,
287 as a 16-bit integer, followed by the bytes representing the
288 characters of the string.
289
290 @type value: C{str}
291 @param value: The string value to be written.
292 """
293 if not isinstance(value, unicode):
294 value = unicode(value, 'utf8')
295
296 buf = util.BufferedByteStream()
297 buf.write_utf8_string(value)
298 bytes = buf.getvalue()
299
300 self.stream.write_ushort(len(bytes))
301 self.stream.write(bytes)
302
304 """
305 Writes a UTF-8 string. Similar to L{writeUTF}, but does
306 not prefix the string with a 16-bit length word.
307
308 @type value: C{str}
309 @param value: The string value to be written.
310 """
311 val = None
312
313 if isinstance(value, unicode):
314 val = value
315 else:
316 val = unicode(value, 'utf8')
317
318 self.stream.write_utf8_string(val)
319
484
485 -class ByteArray(util.BufferedByteStream, DataInput, DataOutput):
486 """
487 I am a C{StringIO} type object containing byte data from the AMF stream.
488 ActionScript 3.0 introduced the C{flash.utils.ByteArray} class to support
489 the manipulation of raw data in the form of an Array of bytes.
490
491 Supports C{zlib} compression.
492
493 Possible uses of the C{ByteArray} class:
494 - Creating a custom protocol to connect to a client.
495 - Writing your own AMF/Remoting packet.
496 - Optimizing the size of your data by using custom data types.
497
498 @see: U{ByteArray on Livedocs (external)
499 <http://livedocs.adobe.com/flex/201/langref/flash/utils/ByteArray.html>}
500 """
509
515
517 buf = self.getvalue()
518
519 if self.compressed:
520 buf = zlib.compress(buf)
521
522 buf = buf[0] + '\xda' + buf[2:]
523
524 return buf
525
527 """
528 Forces compression of the underlying stream.
529 """
530 self.compressed = True
531
532 pyamf.register_class(ByteArray, metadata=['amf3'])
533
535 """
536 I contain meta relating to the class definition.
537
538 @ivar alias: The alias to this class definition. If this value is C{None},
539 or an empty string, the class is considered to be anonymous.
540 @type alias: L{ClassAlias<pyamf.ClassAlias>}
541 @ivar encoding: The type of encoding to use when serializing the object.
542 @type encoding: C{int}
543 """
545 if alias in (None, ''):
546 self.alias = None
547 elif isinstance(alias, pyamf.ClassAlias):
548 self.alias = alias
549 else:
550 self.alias = pyamf.get_class_alias(alias)
551
552 self.encoding = encoding
553
555 if self.alias is None:
556
557 return ''
558
559 if 'anonymous' in self.alias.metadata:
560 return ''
561
562 return str(self.alias)
563
564 name = property(_get_name)
565
567 """
568 If C{alias} is C{None}, an L{anonymous class<pyamf.ASObject>} is
569 returned, otherwise the class is loaded externally.
570 """
571 if self.alias in (None, ''):
572
573 return pyamf.ASObject
574
575 return self.getClassAlias().klass
576
578 """
579 Gets the class alias that is held by this definition.
580
581 @see: L{get_class_alias<pyamf.get_class_alias>}.
582 @rtype: L{ClassAlias<pyamf.ClassAlias>}
583 @return: Class alias.
584 """
585 if not isinstance(self.alias, pyamf.ClassAlias):
586 self.alias = pyamf.get_class_alias(self.alias)
587
588 return self.alias
589
591 """
592 Gets the referenced class that is held by this definition.
593 """
594 if hasattr(self, '_klass'):
595 return self._klass
596
597 self._klass = self._getClass()
598
599 return self._klass
600
601 klass = property(getClass)
602
604 """
605 Returns a list of static attributes based on C{obj}. Once built,
606 this list is immutable.
607
608 @param obj: The object to determine the static attributes from.
609 @type obj: C{mixed}.
610 @since: 0.4
611 """
612 if hasattr(self, 'static_attrs'):
613 return self.static_attrs
614
615 if not self.alias:
616 if hasattr(obj, '__slots__'):
617 return obj.__slots__
618
619 return []
620
621 static_attrs, dynamic_attrs = self.alias.getAttrs(obj, codec=self)
622
623 if static_attrs is None:
624 self.static_attrs = []
625 else:
626 self.static_attrs = static_attrs
627
628 return self.static_attrs
629
631 """
632 Returns a C{tuple} containing a dict of static and dynamic attributes
633 for C{obj}.
634 """
635 if self.alias is not None:
636 return self.alias.getAttributes(obj, codec=codec)
637
638 dynamic_attrs = util.get_attrs(obj)
639 static_attrs = {}
640
641 for a in self.getStaticAttrs(obj, codec=codec):
642 static_attrs[a] = dynamic_attrs[a]
643 del dynamic_attrs[a]
644
645 return static_attrs, dynamic_attrs
646
648 """
649 Creates a new instance.
650 """
651 if self.alias:
652 obj = self.alias.createInstance(codec=codec)
653 else:
654 klass = self.getClass()
655 obj = klass()
656
657 return obj
658
660 """
661 Applies a collection of attributes C{attrs} to object C{obj}
662 """
663 if self.alias:
664 self.alias.applyAttributes(obj, attrs, codec=codec)
665 else:
666 util.set_attrs(obj, attrs)
667
668 -class Context(pyamf.BaseContext):
669 """
670 I hold the AMF3 context for en/decoding streams.
671
672 @ivar strings: A list of string references.
673 @type strings: C{list}
674 @ivar classes: A list of L{ClassDefinition}.
675 @type classes: C{list}
676 @ivar legacy_xml: A list of legacy encoded XML documents.
677 @type legacy_xml: C{list}
678 """
679
680 - def __init__(self):
681 self.strings = util.IndexedCollection(use_hash=True)
682 self.classes = util.IndexedCollection()
683 self.class_defs = util.IndexedCollection()
684 self.legacy_xml = util.IndexedCollection()
685 self.object_aliases = util.IndexedMap()
686
687 pyamf.BaseContext.__init__(self)
688
690 """
691 Clears the context.
692 """
693 pyamf.BaseContext.clear(self)
694
695 self.strings.clear()
696 self.classes.clear()
697 self.class_defs.clear()
698 self.legacy_xml.clear()
699 self.object_aliases.clear()
700
702 """
703 Resets the context.
704
705 @see: L{pyamf.BaseContext.reset}
706 """
707 pyamf.BaseContext.reset(self)
708
709 self.strings.clear()
710 self.classes.clear()
711 self.class_defs.clear()
712 self.legacy_xml.clear()
713 self.object_aliases.clear()
714
715 - def setObjectAlias(self, obj, alias):
716 """
717 Maps an object to an aliased object.
718
719 @since: 0.4
720 """
721 self.object_aliases.map(obj, alias)
722
723 - def getObjectAlias(self, obj):
724 """
725 Get an alias of an object.
726
727 @since: 0.4
728 """
729 ref = self.object_aliases.getReferenceTo(obj)
730 return self.object_aliases.getMappedByReference(ref)
731
732 - def getString(self, ref):
733 """
734 Gets a string based on a reference C{ref}.
735
736 @param ref: The reference index.
737 @type ref: C{str}
738 @raise ReferenceError: The referenced string could not be found.
739
740 @rtype: C{str}
741 @return: The referenced string.
742 """
743 try:
744 return self.strings.getByReference(ref)
745 except pyamf.ReferenceError:
746 raise pyamf.ReferenceError("String reference %r not found" % (ref,))
747
748 - def getStringReference(self, s):
749 """
750 Return string reference.
751
752 @type s: C{str}
753 @param s: The referenced string.
754 @raise ReferenceError: The string reference could not be found.
755 @return: The reference index to the string.
756 @rtype: C{int}
757 """
758 try:
759 return self.strings.getReferenceTo(s)
760 except ValueError:
761 raise pyamf.ReferenceError("Reference for string %r not found" % (s,))
762
763 - def addString(self, s):
764 """
765 Creates a reference to C{s}. If the reference already exists, that
766 reference is returned.
767
768 @type s: C{str}
769 @param s: The string to be referenced.
770 @rtype: C{int}
771 @return: The reference index.
772
773 @raise TypeError: The parameter C{s} is not of C{basestring} type.
774 @raise ValueError: Trying to store a reference to an empty string.
775 """
776 if not isinstance(s, basestring):
777 raise TypeError
778
779 if len(s) == 0:
780
781 raise ValueError("Cannot store a reference to an empty string")
782
783 return self.strings.append(s)
784
785 - def getClassDefinition(self, ref):
786 """
787 Return class reference.
788
789 @raise ReferenceError: The class reference could not be found.
790 @return: Class reference.
791 """
792 try:
793 return self.class_defs.getByReference(ref)
794 except IndexError:
795 raise pyamf.ReferenceError("Class reference %r not found" % (ref,))
796
798 """
799 Return class definition reference.
800
801 @type class_def: L{ClassDefinition} or C{instance} or C{class}
802 @param class_def: The class definition reference to be found.
803 @raise ReferenceError: The reference could not be found.
804 @raise TypeError: Unable to determine class.
805 @return: The reference to C{class_def}.
806 @rtype: C{int}
807 """
808 if isinstance(class_def, ClassDefinition):
809 try:
810 return self.class_defs.getReferenceTo(class_def)
811 except KeyError:
812 raise pyamf.ReferenceError("Reference for class %s not found" % (class_def.klass,))
813
814 if isinstance(class_def, (type, types.ClassType)):
815 try:
816 return self.classes.getReferenceTo(class_def)
817 except pyamf.ReferenceError:
818 raise pyamf.ReferenceError("Reference for class definition for %s not found" % (class_def,))
819 elif isinstance(class_def, (types.InstanceType, types.ObjectType)):
820 try:
821 return self.classes.getReferenceTo(class_def.__class__)
822 except pyamf.ReferenceError:
823 raise pyamf.ReferenceError("Reference for class definition for %s not found" % (class_def.__class__,))
824
825 raise TypeError('Unable to determine class for %r' % (class_def,))
826
827 - def addClassDefinition(self, class_def):
828 """
829 Creates a reference to C{class_def}.
830
831 @param class_def: C{ClassDefinition} instance.
832 """
833 try:
834 return self.class_defs.getReferenceTo(class_def)
835 except pyamf.ReferenceError:
836 try:
837 self.classes.append(class_def.klass)
838 except pyamf.UnknownClassAlias:
839 pass
840
841 return self.class_defs.append(class_def)
842
843 - def removeClassDefinition(self, class_def):
844 """
845 Removes a C{ClassDefinition} reference from this context.
846 """
847 idx = self.rev_classes[id(class_def)]
848
849 del self.rev_classes[id(class_def)]
850 del self.classes[idx]
851
852 - def getLegacyXML(self, ref):
853 """
854 Return the legacy XML reference. This is the C{flash.xml.XMLDocument}
855 class in ActionScript 3.0 and the top-level C{XML} class in
856 ActionScript 1.0 and 2.0.
857
858 @type ref: C{int}
859 @param ref: The reference index.
860 @raise ReferenceError: The legacy XML reference could not be found.
861 @return: Instance of L{ET<util.ET>}
862 """
863 try:
864 return self.legacy_xml.getByReference(ref)
865 except pyamf.ReferenceError:
866 raise pyamf.ReferenceError("Legacy XML reference %r not found" % (ref,))
867
869 """
870 Return legacy XML reference.
871
872 @type doc: L{ET<util.ET>}
873 @param doc: The XML document to reference.
874 @raise ReferenceError: The reference could not be found.
875 @return: The reference to C{doc}.
876 @rtype: C{int}
877 """
878 try:
879 return self.legacy_xml.getReferenceTo(doc)
880 except pyamf.ReferenceError:
881 raise pyamf.ReferenceError("Reference for document %r not found" % (doc,))
882
883 - def addLegacyXML(self, doc):
884 """
885 Creates a reference to C{doc}.
886
887 If C{doc} is already referenced that index will be returned. Otherwise
888 a new index will be created.
889
890 @type doc: L{ET<util.ET>}
891 @param doc: The XML document to reference.
892 @rtype: C{int}
893 @return: The reference to C{doc}.
894 """
895 return self.legacy_xml.append(doc)
896
897 - def __copy__(self):
898 return self.__class__()
899
901 """
902 Decodes an AMF3 data stream.
903 """
904 context_class = Context
905
906 type_map = {
907 ASTypes.UNDEFINED: 'readUndefined',
908 ASTypes.NULL: 'readNull',
909 ASTypes.BOOL_FALSE: 'readBoolFalse',
910 ASTypes.BOOL_TRUE: 'readBoolTrue',
911 ASTypes.INTEGER: 'readSignedInteger',
912 ASTypes.NUMBER: 'readNumber',
913 ASTypes.STRING: 'readString',
914 ASTypes.XML: 'readXML',
915 ASTypes.DATE: 'readDate',
916 ASTypes.ARRAY: 'readArray',
917 ASTypes.OBJECT: 'readObject',
918 ASTypes.XMLSTRING: 'readXMLString',
919 ASTypes.BYTEARRAY: 'readByteArray',
920 }
921
922 - def __init__(self, data=None, context=None, strict=False, use_proxies=None):
929
931 """
932 Read and returns the next byte in the stream and determine its type.
933
934 @raise DecodeError: AMF3 type not recognized.
935 @return: AMF3 type.
936 @rtype: C{int}
937 """
938 type = self.stream.read_uchar()
939
940 if type not in ACTIONSCRIPT_TYPES:
941 raise pyamf.DecodeError("Unknown AMF3 type 0x%02x at %d" % (type, self.stream.tell() - 1))
942
943 return type
944
950
952 """
953 Read null.
954
955 @return: C{None}
956 @rtype: C{None}
957 """
958 return None
959
961 """
962 Returns C{False}.
963
964 @return: C{False}
965 @rtype: C{bool}
966 """
967 return False
968
970 """
971 Returns C{True}.
972
973 @return: C{True}
974 @rtype: C{bool}
975 """
976 return True
977
979 """
980 Read number.
981 """
982 return self.stream.read_double()
983
985 """
986 Reads and returns an unsigned integer from the stream.
987 """
988 return self.readInteger(False)
989
991 """
992 Reads and returns a signed integer from the stream.
993 """
994 return self.readInteger(True)
995
997 """
998 Reads and returns an integer from the stream.
999
1000 @type signed: C{bool}
1001 @see: U{Parsing integers on OSFlash
1002 <http://osflash.org/amf3/parsing_integers>} for the AMF3 integer data
1003 format.
1004 """
1005 return decode_int(self.stream, signed)
1006
1008 """
1009 Reads and returns a string from the stream.
1010
1011 @type use_references: C{bool}
1012 """
1013 def readLength():
1014 x = self.readUnsignedInteger()
1015
1016 return (x >> 1, x & REFERENCE_BIT == 0)
1017
1018 length, is_reference = readLength()
1019
1020 if use_references and is_reference:
1021 return self.context.getString(length)
1022
1023 buf = self.stream.read(length)
1024 result = unicode(buf, "utf8")
1025
1026 if len(result) != 0 and use_references:
1027 self.context.addString(result)
1028
1029 return result
1030
1048
1050 """
1051 Reads an array from the stream.
1052
1053 @warning: There is a very specific problem with AMF3 where the first
1054 three bytes of an encoded empty C{dict} will mirror that of an encoded
1055 C{{'': 1, '2': 2}}
1056
1057 @see: U{Docuverse blog (external)
1058 <http://www.docuverse.com/blog/donpark/2007/05/14/flash-9-amf3-bug>}
1059 """
1060 size = self.readUnsignedInteger()
1061
1062 if size & REFERENCE_BIT == 0:
1063 return self.context.getObject(size >> 1)
1064
1065 size >>= 1
1066
1067 key = self.readString().encode('utf8')
1068
1069 if key == "":
1070
1071 result = []
1072 self.context.addObject(result)
1073
1074 for i in xrange(size):
1075 result.append(self.readElement())
1076
1077 else:
1078 result = pyamf.MixedArray()
1079 self.context.addObject(result)
1080
1081 while key != "":
1082 el = self.readElement()
1083
1084 try:
1085 result[str(key)] = el
1086 except UnicodeError:
1087 result[key] = el
1088
1089 key = self.readString().encode('utf8')
1090
1091 for i in xrange(size):
1092 el = self.readElement()
1093 result[i] = el
1094
1095 return result
1096
1125
1127 """
1128 Reads an object from the stream.
1129
1130 @raise pyamf.EncodeError: Decoding an object in amf3 tagged as amf0
1131 only is not allowed.
1132 @raise pyamf.DecodeError: Unknown object encoding.
1133 """
1134 if _use_proxies is None:
1135 _use_proxies = self.use_proxies
1136
1137 def readStatic(is_ref, class_def, obj, num_attrs):
1138 if not is_ref:
1139 class_def.static_attrs = []
1140
1141 for i in range(num_attrs):
1142 key = self.readString().encode('utf8')
1143
1144 class_def.static_attrs.append(key)
1145
1146 for attr in class_def.static_attrs:
1147 obj[attr] = self.readElement()
1148
1149 def readDynamic(is_ref, class_def, obj):
1150 attr = self.readString().encode('utf8')
1151
1152 while attr != '':
1153 obj[attr] = self.readElement()
1154 attr = self.readString().encode('utf8')
1155
1156 ref = self.readUnsignedInteger()
1157
1158 if ref & REFERENCE_BIT == 0:
1159 obj = self.context.getObject(ref >> 1)
1160
1161 if _use_proxies is True:
1162 obj = self.readProxyObject(obj)
1163
1164 return obj
1165
1166 ref >>= 1
1167
1168 class_ref, class_def, num_attrs = self._getClassDefinition(ref)
1169
1170 if class_def.alias and 'amf0' in class_def.alias.metadata:
1171 raise pyamf.EncodeError("Decoding an object in amf3 tagged as amf0 only is not allowed")
1172
1173 obj = class_def.createInstance(codec=self)
1174 obj_attrs = dict()
1175
1176 self.context.addObject(obj)
1177
1178 if class_def.encoding in (ObjectEncoding.EXTERNAL, ObjectEncoding.PROXY):
1179 obj.__readamf__(DataInput(self))
1180 elif class_def.encoding == ObjectEncoding.DYNAMIC:
1181 readStatic(class_ref, class_def, obj_attrs, num_attrs)
1182 readDynamic(class_ref, class_def, obj_attrs)
1183 elif class_def.encoding == ObjectEncoding.STATIC:
1184 readStatic(class_ref, class_def, obj_attrs, num_attrs)
1185 else:
1186 raise pyamf.DecodeError("Unknown object encoding")
1187
1188 class_def.applyAttributes(obj, obj_attrs, codec=self)
1189
1190 if _use_proxies is True:
1191 obj = self.readProxyObject(obj)
1192
1193 return obj
1194
1196 """
1197 Return the source object of a proxied object.
1198
1199 @since: 0.4
1200 """
1201 if isinstance(proxy, ArrayCollection):
1202 return list(proxy)
1203 elif isinstance(proxy, ObjectProxy):
1204 return proxy._amf_object
1205 else:
1206 return proxy
1207
1209 """
1210 Reads an object from the stream.
1211
1212 @type legacy: C{bool}
1213 @param legacy: The read XML is in 'legacy' format.
1214 """
1215 ref = self.readUnsignedInteger()
1216
1217 if ref & REFERENCE_BIT == 0:
1218 return self.context.getObject(ref >> 1)
1219
1220 xmlstring = self.stream.read(ref >> 1)
1221
1222 x = util.ET.XML(xmlstring)
1223 self.context.addObject(x)
1224
1225 if legacy is True:
1226 self.context.addLegacyXML(x)
1227
1228 return x
1229
1231 """
1232 Reads a string from the data stream and converts it into
1233 an XML Tree.
1234
1235 @return: The XML Document.
1236 @rtype: L{ET<util.ET>}
1237 """
1238 return self._readXML()
1239
1241 """
1242 Read a legacy XML Document from the stream.
1243
1244 @return: The XML Document.
1245 @rtype: L{ET<util.ET>}
1246 """
1247 return self._readXML(True)
1248
1250 """
1251 Reads a string of data from the stream.
1252
1253 Detects if the L{ByteArray} was compressed using C{zlib}.
1254
1255 @see: L{ByteArray}
1256 @note: This is not supported in ActionScript 1.0 and 2.0.
1257 """
1258 ref = self.readUnsignedInteger()
1259
1260 if ref & REFERENCE_BIT == 0:
1261 return self.context.getObject(ref >> 1)
1262
1263 buffer = self.stream.read(ref >> 1)
1264
1265 try:
1266 buffer = zlib.decompress(buffer)
1267 compressed = True
1268 except zlib.error:
1269 compressed = False
1270
1271 obj = ByteArray(buffer, context=self.context)
1272 obj.compressed = compressed
1273
1274 self.context.addObject(obj)
1275
1276 return obj
1277
1279 """
1280 Encodes an AMF3 data stream.
1281 """
1282 context_class = Context
1283
1284 type_map = [
1285 ((types.BuiltinFunctionType, types.BuiltinMethodType,
1286 types.FunctionType, types.GeneratorType, types.ModuleType,
1287 types.LambdaType, types.MethodType), "writeFunc"),
1288 ((bool,), "writeBoolean"),
1289 ((types.NoneType,), "writeNull"),
1290 ((int,long), "writeInteger"),
1291 ((float,), "writeNumber"),
1292 ((types.StringTypes,), "writeString"),
1293 ((ByteArray,), "writeByteArray"),
1294 ((datetime.date, datetime.datetime), "writeDate"),
1295 ((util.is_ET_element,), "writeXML"),
1296 ((lambda x: x is pyamf.Undefined,), "writeUndefined"),
1297 ((types.InstanceType, types.ObjectType,), "writeInstance"),
1298 ]
1299
1300 - def __init__(self, data=None, context=None, strict=False, use_proxies=None):
1307
1309 """
1310 Writes the data.
1311
1312 @type data: C{mixed}
1313 @param data: The data to be encoded to the AMF3 data stream.
1314 @type use_references: C{bool}
1315 @param use_references: Default is C{True}.
1316 @raise EncodeError: Cannot find encoder func for C{data}.
1317 """
1318 func = self._writeElementFunc(data)
1319
1320 if func is None:
1321 raise pyamf.EncodeError("Cannot find encoder func for %r" % (data,))
1322 else:
1323 try:
1324 if isinstance(func, pyamf.CustomTypeFunc):
1325 func(data)
1326 else:
1327 func(data, use_references=use_references)
1328 except (KeyboardInterrupt, SystemExit):
1329 raise
1330 except:
1331 raise
1332
1334 """
1335 Writes the data type to the stream.
1336
1337 @param type: ActionScript type.
1338 @raise EncodeError: AMF3 type is not recognized.
1339 @see: L{ACTIONSCRIPT_TYPES}
1340 """
1341 if type not in ACTIONSCRIPT_TYPES:
1342 raise pyamf.EncodeError("Unknown AMF3 type 0x%02x at %d" % (
1343 type, self.stream.tell() - 1))
1344
1345 self.stream.write_uchar(type)
1346
1348 """
1349 Functions cannot be serialised.
1350 """
1351 raise pyamf.EncodeError("Callables cannot be serialised")
1352
1354 """
1355 Writes an C{pyamf.Undefined} value to the stream.
1356
1357 @param d: The C{undefined} data to be encoded to the AMF3 data stream.
1358 @type use_references: C{bool}
1359 @param use_references: Default is C{True}.
1360 """
1361 self.writeType(ASTypes.UNDEFINED)
1362
1363 - def writeNull(self, n, use_references=True):
1364 """
1365 Writes a C{null} value to the stream.
1366
1367 @param n: The C{null} data to be encoded to the AMF3 data stream.
1368 @type n: C{null} data.
1369 @type use_references: C{bool}
1370 @param use_references: Default is C{True}.
1371 """
1372 self.writeType(ASTypes.NULL)
1373
1375 """
1376 Writes a Boolean to the stream.
1377
1378 @param n: The C{boolean} data to be encoded to the AMF3 data stream.
1379 @type n: C{bool}
1380 @type use_references: C{bool}
1381 @param use_references: Default is C{True}.
1382 """
1383 if n:
1384 self.writeType(ASTypes.BOOL_TRUE)
1385 else:
1386 self.writeType(ASTypes.BOOL_FALSE)
1387
1389 """
1390 AMF3 integers are encoded.
1391
1392 @param n: The integer data to be encoded to the AMF3 data stream.
1393 @type n: integer data
1394
1395 @see: U{Parsing Integers on OSFlash
1396 <http://osflash.org/documentation/amf3/parsing_integers>}
1397 for more info.
1398 """
1399 self.stream.write(encode_int(n))
1400
1402 """
1403 Writes an integer to the stream.
1404
1405 @type n: integer data
1406 @param n: The integer data to be encoded to the AMF3 data stream.
1407 @type use_references: C{bool}
1408 @param use_references: Default is C{True}.
1409 """
1410 if n & 0xf0000000 not in [0, 0xf0000000]:
1411 self.writeNumber(n)
1412
1413 return
1414
1415 self.writeType(ASTypes.INTEGER)
1416 self.stream.write(encode_int(n))
1417
1419 """
1420 Writes a non integer to the stream.
1421
1422 @type n: number data
1423 @param n: The number data to be encoded to the AMF3 data stream.
1424 @type use_references: C{bool}
1425 @param use_references: Default is C{True}
1426 """
1427 self.writeType(ASTypes.NUMBER)
1428 self.stream.write_double(n)
1429
1431 """
1432 Writes a raw string to the stream.
1433
1434 @type n: C{str} or C{unicode}
1435 @param n: The string data to be encoded to the AMF3 data stream.
1436 @type use_references: C{bool}
1437 @param use_references: Default is C{True}.
1438 """
1439 if not isinstance(n, basestring):
1440 bytes = unicode(n).encode('utf8')
1441 n = bytes
1442 elif isinstance(n, unicode):
1443 bytes = n.encode('utf8')
1444 else:
1445 bytes = n
1446
1447 if len(bytes) == 0:
1448 self._writeInteger(REFERENCE_BIT)
1449
1450 return
1451
1452 if use_references:
1453 try:
1454 ref = self.context.getStringReference(n)
1455 self._writeInteger(ref << 1)
1456
1457 return
1458 except pyamf.ReferenceError:
1459 self.context.addString(n)
1460
1461 self._writeInteger((len(bytes) << 1) | REFERENCE_BIT)
1462 self.stream.write(bytes)
1463
1465 """
1466 Writes a string to the stream. If C{n} is not a unicode string, an
1467 attempt will be made to convert it.
1468
1469 @type n: C{basestring}
1470 @param n: The string data to be encoded to the AMF3 data stream.
1471 @type use_references: C{bool}
1472 @param use_references: Default is C{True}.
1473 """
1474 self.writeType(ASTypes.STRING)
1475 self._writeString(n, use_references)
1476
1477 - def writeDate(self, n, use_references=True):
1501
1502 - def writeList(self, n, use_references=True, _use_proxies=None):
1503 """
1504 Writes a C{tuple}, C{set} or C{list} to the stream.
1505
1506 @type n: One of C{__builtin__.tuple}, C{__builtin__.set}
1507 or C{__builtin__.list}
1508 @param n: The C{list} data to be encoded to the AMF3 data stream.
1509 @type use_references: C{bool}
1510 @param use_references: Default is C{True}.
1511 """
1512
1513 if _use_proxies is None:
1514 _use_proxies = self.use_proxies
1515
1516 if _use_proxies is True:
1517 try:
1518 ref_obj = self.context.getObjectAlias(n)
1519 except pyamf.ReferenceError:
1520 proxy = ArrayCollection(n)
1521 self.context.setObjectAlias(n, proxy)
1522 ref_obj = proxy
1523
1524 self.writeObject(ref_obj, use_references)
1525
1526 return
1527
1528 self.writeType(ASTypes.ARRAY)
1529
1530 if use_references is True:
1531 try:
1532 ref = self.context.getObjectReference(n)
1533 self._writeInteger(ref << 1)
1534
1535 return
1536 except pyamf.ReferenceError:
1537 self.context.addObject(n)
1538
1539 self._writeInteger(len(n) << 1 | REFERENCE_BIT)
1540
1541 self.stream.write_uchar(0x01)
1542
1543 for x in n:
1544 self.writeElement(x)
1545
1546 - def writeDict(self, n, use_references=True, _use_proxies=None):
1547 """
1548 Writes a C{dict} to the stream.
1549
1550 @type n: C{__builtin__.dict}
1551 @param n: The C{dict} data to be encoded to the AMF3 data stream.
1552 @type use_references: C{bool}
1553 @param use_references: Default is C{True}.
1554 @raise ValueError: Non C{int}/C{str} key value found in the C{dict}
1555 @raise EncodeError: C{dict} contains empty string keys.
1556 """
1557
1558
1559
1560
1561 if n.has_key(''):
1562 raise pyamf.EncodeError("dicts cannot contain empty string keys")
1563
1564 if _use_proxies is None:
1565 _use_proxies = self.use_proxies
1566
1567 if _use_proxies is True:
1568 try:
1569 ref_obj = self.context.getObjectAlias(n)
1570 except pyamf.ReferenceError:
1571 dictObj = pyamf.ASObject(n)
1572 proxy = ObjectProxy(dictObj)
1573 self.context.setObjectAlias(n, proxy)
1574 ref_obj = proxy
1575
1576 self.writeObject(ref_obj, use_references)
1577
1578 return
1579
1580 self.writeType(ASTypes.ARRAY)
1581
1582 if use_references:
1583 try:
1584 ref = self.context.getObjectReference(n)
1585 self._writeInteger(ref << 1)
1586
1587 return
1588 except pyamf.ReferenceError:
1589 self.context.addObject(n)
1590
1591
1592 keys = n.keys()
1593 int_keys = []
1594 str_keys = []
1595
1596 for x in keys:
1597 if isinstance(x, (int, long)):
1598 int_keys.append(x)
1599 elif isinstance(x, (str, unicode)):
1600 str_keys.append(x)
1601 else:
1602 raise ValueError("Non int/str key value found in dict")
1603
1604
1605 l = len(int_keys)
1606
1607 for x in int_keys:
1608 if l < x <= 0:
1609
1610 str_keys.append(x)
1611 del int_keys[int_keys.index(x)]
1612
1613 int_keys.sort()
1614
1615
1616 if len(int_keys) > 0 and int_keys[0] != 0:
1617 for x in int_keys:
1618 str_keys.append(str(x))
1619 del int_keys[int_keys.index(x)]
1620
1621 self._writeInteger(len(int_keys) << 1 | REFERENCE_BIT)
1622
1623 for x in str_keys:
1624 self._writeString(x)
1625 self.writeElement(n[x])
1626
1627 self.stream.write_uchar(0x01)
1628
1629 for k in int_keys:
1630 self.writeElement(n[k])
1631
1651
1653 """
1654 Read class definition.
1655
1656 @param obj: The class instance data to be encoded to the AMF3
1657 data stream.
1658 @type obj: instance data
1659 @type use_references: C{bool}
1660 @param use_references: Default is C{True}.
1661 """
1662 if obj.__class__ in (pyamf.MixedArray,):
1663 self.writeDict(obj, use_references)
1664 elif obj.__class__ in (list, set, tuple):
1665 self.writeList(obj, use_references)
1666 else:
1667 self.writeObject(obj, use_references)
1668
1669 - def writeObject(self, obj, use_references=True, _use_proxies=None):
1750
1752 """
1753 Writes a L{ByteArray} to the data stream.
1754
1755 @type n: L{ByteArray}
1756 @param n: The L{ByteArray} data to be encoded to the AMF3 data stream.
1757 @type use_references: C{bool}
1758 @param use_references: Default is C{True}.
1759 """
1760 self.writeType(ASTypes.BYTEARRAY)
1761
1762 if use_references:
1763 try:
1764 ref = self.context.getObjectReference(n)
1765 self._writeInteger(ref << 1)
1766
1767 return
1768 except pyamf.ReferenceError:
1769 self.context.addObject(n)
1770
1771 buf = str(n)
1772 l = len(buf)
1773 self._writeInteger(l << 1 | REFERENCE_BIT)
1774 self.stream.write(buf)
1775
1776 - def writeXML(self, n, use_references=True):
1806
1807 -def decode(stream, context=None, strict=False):
1808 """
1809 A helper function to decode an AMF3 datastream.
1810
1811 @type stream: L{BufferedByteStream<util.BufferedByteStream>}
1812 @param stream: AMF3 data.
1813 @type context: L{Context}
1814 @param context: Context.
1815 """
1816 decoder = Decoder(stream, context, strict)
1817
1818 while 1:
1819 try:
1820 yield decoder.readElement()
1821 except pyamf.EOStream:
1822 break
1823
1825 """
1826 A helper function to encode an element into AMF3 format.
1827
1828 @type args: List of args to encode.
1829 @keyword context: Any initial context to use.
1830 @type context: L{Context}
1831 @return: C{StringIO} type object containing the encoded AMF3 data.
1832 @rtype: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
1833 """
1834 context = kwargs.get('context', None)
1835 buf = util.BufferedByteStream()
1836 encoder = Encoder(buf, context)
1837
1838 for element in args:
1839 encoder.writeElement(element)
1840
1841 return buf
1842
1844 """
1845 @raise OverflowError: Out of range.
1846 """
1847 if n & 0xf0000000 not in [0, 0xf0000000]:
1848 raise OverflowError("Out of range")
1849
1850 bytes = ''
1851 real_value = None
1852
1853 if n < 0:
1854 n += 0x20000000
1855
1856 if n > 0x1fffff:
1857 real_value = n
1858 n >>= 1
1859 bytes += chr(0x80 | ((n >> 21) & 0xff))
1860
1861 if n > 0x3fff:
1862 bytes += chr(0x80 | ((n >> 14) & 0xff))
1863
1864 if n > 0x7f:
1865 bytes += chr(0x80 | ((n >> 7) & 0xff))
1866
1867 if real_value is not None:
1868 n = real_value
1869
1870 if n > 0x1fffff:
1871 bytes += chr(n & 0xff)
1872 else:
1873 bytes += chr(n & 0x7f)
1874
1875 return bytes
1876
1902
1903 try:
1904 from cpyamf.amf3 import encode_int
1905 except ImportError:
1906 encode_int = _encode_int
1907 try:
1908 from cpyamf.amf3 import decode_int
1909 except ImportError:
1910 decode_int = _decode_int
1911