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)
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)
637
638 dynamic_attrs = util.get_attrs(obj)
639 static_attrs = {}
640
641 for a in self.getStaticAttrs(obj):
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()
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)
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 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()
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()
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()
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()
1151
1152 while attr != '':
1153 obj[attr] = self.readElement()
1154 attr = self.readString()
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()
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)
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 @raise EncodeError: Unable to encode data.
1318 """
1319 func = self._writeElementFunc(data)
1320
1321 if func is None:
1322 raise pyamf.EncodeError("Cannot find encoder func for %r" % (data,))
1323 else:
1324 try:
1325 if isinstance(func, pyamf.CustomTypeFunc):
1326 func(data)
1327 else:
1328 func(data, use_references=use_references)
1329 except (KeyboardInterrupt, SystemExit):
1330 raise
1331 except:
1332 raise
1333
1335 """
1336 Writes the data type to the stream.
1337
1338 @param type: ActionScript type.
1339 @raise EncodeError: AMF3 type is not recognized.
1340 @see: L{ACTIONSCRIPT_TYPES}
1341 """
1342 if type not in ACTIONSCRIPT_TYPES:
1343 raise pyamf.EncodeError("Unknown AMF3 type 0x%02x at %d" % (
1344 type, self.stream.tell() - 1))
1345
1346 self.stream.write_uchar(type)
1347
1349 """
1350 Functions cannot be serialised.
1351 """
1352 raise pyamf.EncodeError("Callables cannot be serialised")
1353
1355 """
1356 Writes an C{pyamf.Undefined} value to the stream.
1357
1358 @param d: The C{undefined} data to be encoded to the AMF3 data stream.
1359 @type use_references: C{bool}
1360 @param use_references: Default is C{True}.
1361 """
1362 self.writeType(ASTypes.UNDEFINED)
1363
1364 - def writeNull(self, n, use_references=True):
1365 """
1366 Writes a C{null} value to the stream.
1367
1368 @param n: The C{null} data to be encoded to the AMF3 data stream.
1369 @type n: C{null} data.
1370 @type use_references: C{bool}
1371 @param use_references: Default is C{True}.
1372 """
1373 self.writeType(ASTypes.NULL)
1374
1376 """
1377 Writes a Boolean to the stream.
1378
1379 @param n: The C{boolean} data to be encoded to the AMF3 data stream.
1380 @type n: C{bool}
1381 @type use_references: C{bool}
1382 @param use_references: Default is C{True}.
1383 """
1384 if n:
1385 self.writeType(ASTypes.BOOL_TRUE)
1386 else:
1387 self.writeType(ASTypes.BOOL_FALSE)
1388
1390 """
1391 AMF3 integers are encoded.
1392
1393 @param n: The integer data to be encoded to the AMF3 data stream.
1394 @type n: integer data
1395
1396 @see: U{Parsing Integers on OSFlash
1397 <http://osflash.org/documentation/amf3/parsing_integers>}
1398 for more info.
1399 """
1400 self.stream.write(encode_int(n))
1401
1403 """
1404 Writes an integer to the stream.
1405
1406 @type n: integer data
1407 @param n: The integer data to be encoded to the AMF3 data stream.
1408 @type use_references: C{bool}
1409 @param use_references: Default is C{True}.
1410 """
1411 if n & 0xf0000000 not in [0, 0xf0000000]:
1412 self.writeNumber(n)
1413
1414 return
1415
1416 self.writeType(ASTypes.INTEGER)
1417 self.stream.write(encode_int(n))
1418
1420 """
1421 Writes a non integer to the stream.
1422
1423 @type n: number data
1424 @param n: The number data to be encoded to the AMF3 data stream.
1425 @type use_references: C{bool}
1426 @param use_references: Default is C{True}
1427 """
1428 self.writeType(ASTypes.NUMBER)
1429 self.stream.write_double(n)
1430
1432 """
1433 Writes a raw string to the stream.
1434
1435 @type n: C{str} or C{unicode}
1436 @param n: The string data to be encoded to the AMF3 data stream.
1437 @type use_references: C{bool}
1438 @param use_references: Default is C{True}.
1439 """
1440 if not isinstance(n, basestring):
1441 bytes = unicode(n).encode('utf8')
1442 n = bytes
1443 elif isinstance(n, unicode):
1444 bytes = n.encode('utf8')
1445 else:
1446 bytes = n
1447
1448 if len(bytes) == 0:
1449 self._writeInteger(REFERENCE_BIT)
1450
1451 return
1452
1453 if use_references:
1454 try:
1455 ref = self.context.getStringReference(n)
1456 self._writeInteger(ref << 1)
1457
1458 return
1459 except pyamf.ReferenceError:
1460 self.context.addString(n)
1461
1462 self._writeInteger((len(bytes) << 1) | REFERENCE_BIT)
1463 self.stream.write(bytes)
1464
1466 """
1467 Writes a string to the stream. If C{n} is not a unicode string, an
1468 attempt will be made to convert it.
1469
1470 @type n: C{basestring}
1471 @param n: The string data to be encoded to the AMF3 data stream.
1472 @type use_references: C{bool}
1473 @param use_references: Default is C{True}.
1474 """
1475 self.writeType(ASTypes.STRING)
1476 self._writeString(n, use_references)
1477
1478 - def writeDate(self, n, use_references=True):
1502
1503 - def writeList(self, n, use_references=True, _use_proxies=None):
1504 """
1505 Writes a C{tuple}, C{set} or C{list} to the stream.
1506
1507 @type n: One of C{__builtin__.tuple}, C{__builtin__.set}
1508 or C{__builtin__.list}
1509 @param n: The C{list} data to be encoded to the AMF3 data stream.
1510 @type use_references: C{bool}
1511 @param use_references: Default is C{True}.
1512 """
1513
1514 if _use_proxies is None:
1515 _use_proxies = self.use_proxies
1516
1517 if _use_proxies is True:
1518 try:
1519 ref_obj = self.context.getObjectAlias(n)
1520 except pyamf.ReferenceError:
1521 proxy = ArrayCollection(n)
1522 self.context.setObjectAlias(n, proxy)
1523 ref_obj = proxy
1524
1525 self.writeObject(ref_obj, use_references)
1526
1527 return
1528
1529 self.writeType(ASTypes.ARRAY)
1530
1531 if use_references is True:
1532 try:
1533 ref = self.context.getObjectReference(n)
1534 self._writeInteger(ref << 1)
1535
1536 return
1537 except pyamf.ReferenceError:
1538 self.context.addObject(n)
1539
1540 self._writeInteger(len(n) << 1 | REFERENCE_BIT)
1541
1542 self.stream.write_uchar(0x01)
1543
1544 for x in n:
1545 self.writeElement(x)
1546
1547 - def writeDict(self, n, use_references=True, _use_proxies=None):
1548 """
1549 Writes a C{dict} to the stream.
1550
1551 @type n: C{__builtin__.dict}
1552 @param n: The C{dict} data to be encoded to the AMF3 data stream.
1553 @type use_references: C{bool}
1554 @param use_references: Default is C{True}.
1555 @raise ValueError: Non C{int}/C{str} key value found in the C{dict}
1556 @raise EncodeError: C{dict} contains empty string keys.
1557 """
1558
1559
1560
1561
1562 if n.has_key(''):
1563 raise pyamf.EncodeError("dicts cannot contain empty string keys")
1564
1565 if _use_proxies is None:
1566 _use_proxies = self.use_proxies
1567
1568 if _use_proxies is True:
1569 try:
1570 ref_obj = self.context.getObjectAlias(n)
1571 except pyamf.ReferenceError:
1572 dictObj = pyamf.ASObject(n)
1573 proxy = ObjectProxy(dictObj)
1574 self.context.setObjectAlias(n, proxy)
1575 ref_obj = proxy
1576
1577 self.writeObject(ref_obj, use_references)
1578
1579 return
1580
1581 self.writeType(ASTypes.ARRAY)
1582
1583 if use_references:
1584 try:
1585 ref = self.context.getObjectReference(n)
1586 self._writeInteger(ref << 1)
1587
1588 return
1589 except pyamf.ReferenceError:
1590 self.context.addObject(n)
1591
1592
1593 keys = n.keys()
1594 int_keys = []
1595 str_keys = []
1596
1597 for x in keys:
1598 if isinstance(x, (int, long)):
1599 int_keys.append(x)
1600 elif isinstance(x, (str, unicode)):
1601 str_keys.append(x)
1602 else:
1603 raise ValueError("Non int/str key value found in dict")
1604
1605
1606 l = len(int_keys)
1607
1608 for x in int_keys:
1609 if l < x <= 0:
1610
1611 str_keys.append(x)
1612 del int_keys[int_keys.index(x)]
1613
1614 int_keys.sort()
1615
1616
1617 if len(int_keys) > 0 and int_keys[0] != 0:
1618 for x in int_keys:
1619 str_keys.append(str(x))
1620 del int_keys[int_keys.index(x)]
1621
1622 self._writeInteger(len(int_keys) << 1 | REFERENCE_BIT)
1623
1624 for x in str_keys:
1625 self._writeString(x)
1626 self.writeElement(n[x])
1627
1628 self.stream.write_uchar(0x01)
1629
1630 for k in int_keys:
1631 self.writeElement(n[k])
1632
1634 """
1635 Builds a class definition based on the C{obj}.
1636
1637 @raise pyamf.EncodeError: Unable to determine object attributes.
1638 """
1639 encoding = ObjectEncoding.DYNAMIC
1640
1641 alias = self.context.getClassAlias(obj.__class__)
1642
1643 if alias:
1644 if 'dynamic' in alias.metadata:
1645 encoding = ObjectEncoding.DYNAMIC
1646 elif 'static' in alias.metadata:
1647 encoding = ObjectEncoding.STATIC
1648 elif 'external' in alias.metadata:
1649 encoding = ObjectEncoding.EXTERNAL
1650
1651 class_def = ClassDefinition(alias, encoding)
1652
1653 return class_def
1654
1656 """
1657 Read class definition.
1658
1659 @param obj: The class instance data to be encoded to the AMF3
1660 data stream.
1661 @type obj: instance data
1662 @type use_references: C{bool}
1663 @param use_references: Default is C{True}.
1664 """
1665 if obj.__class__ in (pyamf.MixedArray,):
1666 self.writeDict(obj, use_references)
1667 elif obj.__class__ in (list, set, tuple):
1668 self.writeList(obj, use_references)
1669 else:
1670 self.writeObject(obj, use_references)
1671
1672 - def writeObject(self, obj, use_references=True, _use_proxies=None):
1753
1755 """
1756 Writes a L{ByteArray} to the data stream.
1757
1758 @type n: L{ByteArray}
1759 @param n: The L{ByteArray} data to be encoded to the AMF3 data stream.
1760 @type use_references: C{bool}
1761 @param use_references: Default is C{True}.
1762 """
1763 self.writeType(ASTypes.BYTEARRAY)
1764
1765 if use_references:
1766 try:
1767 ref = self.context.getObjectReference(n)
1768 self._writeInteger(ref << 1)
1769
1770 return
1771 except pyamf.ReferenceError:
1772 self.context.addObject(n)
1773
1774 buf = str(n)
1775 l = len(buf)
1776 self._writeInteger(l << 1 | REFERENCE_BIT)
1777 self.stream.write(buf)
1778
1779 - def writeXML(self, n, use_references=True):
1809
1810 -def decode(stream, context=None, strict=False):
1811 """
1812 A helper function to decode an AMF3 datastream.
1813
1814 @type stream: L{BufferedByteStream<util.BufferedByteStream>}
1815 @param stream: AMF3 data.
1816 @type context: L{Context}
1817 @param context: Context.
1818 """
1819 decoder = Decoder(stream, context, strict)
1820
1821 while 1:
1822 try:
1823 yield decoder.readElement()
1824 except pyamf.EOStream:
1825 break
1826
1828 """
1829 A helper function to encode an element into AMF3 format.
1830
1831 @type args: List of args to encode.
1832 @keyword context: Any initial context to use.
1833 @type context: L{Context}
1834 @return: C{StringIO} type object containing the encoded AMF3 data.
1835 @rtype: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
1836 """
1837 context = kwargs.get('context', None)
1838 buf = util.BufferedByteStream()
1839 encoder = Encoder(buf, context)
1840
1841 for element in args:
1842 encoder.writeElement(element)
1843
1844 return buf
1845
1847 """
1848 @raise OverflowError: Out of range.
1849 """
1850 if n & 0xf0000000 not in [0, 0xf0000000]:
1851 raise OverflowError("Out of range")
1852
1853 bytes = ''
1854 real_value = None
1855
1856 if n < 0:
1857 n += 0x20000000
1858
1859 if n > 0x1fffff:
1860 real_value = n
1861 n >>= 1
1862 bytes += chr(0x80 | ((n >> 21) & 0xff))
1863
1864 if n > 0x3fff:
1865 bytes += chr(0x80 | ((n >> 14) & 0xff))
1866
1867 if n > 0x7f:
1868 bytes += chr(0x80 | ((n >> 7) & 0xff))
1869
1870 if real_value is not None:
1871 n = real_value
1872
1873 if n > 0x1fffff:
1874 bytes += chr(n & 0xff)
1875 else:
1876 bytes += chr(n & 0x7f)
1877
1878 return bytes
1879
1905
1906 try:
1907 from cpyamf.amf3 import encode_int
1908 except ImportError:
1909 encode_int = _encode_int
1910 try:
1911 from cpyamf.amf3 import decode_int
1912 except ImportError:
1913 decode_int = _decode_int
1914