2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 #include "AMFConverter.h"
23 #include "SimpleBuffer.h"
25 #include "namedStrings.h"
27 #include "as_object.h"
28 #include "ObjectURI.h"
33 #include "Global_as.h"
35 #include "as_function.h"
36 #include "PropertyList.h"
38 // Define this macro to make AMF parsing verbose
39 //#define GNASH_DEBUG_AMF_DESERIALIZE 1
41 // Define this macto to make AMF writing verbose
42 // #define GNASH_DEBUG_AMF_SERIALIZE 1
49 /// Class used to serialize properties of an object to a buffer
50 class ObjectSerializer
: public PropertyVisitor
54 ObjectSerializer(Writer
& w
, VM
& vm
)
57 _st(vm
.getStringTable()),
61 bool success() const { return !_error
; }
63 virtual bool accept(const ObjectURI
& uri
, const as_value
& val
) {
65 if (_error
) return true;
67 // Tested with SharedObject and AMFPHP
68 if (val
.is_function()) {
69 log_debug("AMF0: skip serialization of FUNCTION property");
73 const string_table::key key
= getName(uri
);
75 // Test conducted with AMFPHP:
76 // '__proto__' and 'constructor' members
77 // of an object don't get back from an 'echo-service'.
78 // Dunno if they are not serialized or just not sent back.
79 // A '__constructor__' member gets back, but only if
80 // not a function. Actually no function gets back.
81 if (key
== NSV::PROP_uuPROTOuu
|| key
== NSV::PROP_CONSTRUCTOR
)
83 #ifdef GNASH_DEBUG_AMF_SERIALIZE
84 log_debug(" skip serialization of specially-named property %s",
90 // write property name
91 const std::string
& name
= _st
.value(key
);
93 #ifdef GNASH_DEBUG_AMF_SERIALIZE
94 log_debug(" serializing property %s", name
);
96 _writer
.writePropertyName(name
);
97 if (!val
.writeAMF0(_writer
)) {
98 log_error("Problems serializing an object's member");
115 Writer::writePropertyName(const std::string
& name
)
117 writePlainString(_buf
, name
, STRING_AMF0
);
122 Writer::writeObject(as_object
* obj
)
126 // This probably shouldn't happen.
127 if (obj
->to_function()) return false;
129 OffsetTable::iterator it
= _offsets
.find(obj
);
131 // Handle references first.
132 if (it
!= _offsets
.end()) {
133 const size_t idx
= it
->second
;
134 #ifdef GNASH_DEBUG_AMF_SERIALIZE
135 log_debug(_("amf: serializing object (or function) "
136 "as reference to %d"), idx
);
138 _buf
.appendByte(REFERENCE_AMF0
);
139 _buf
.appendNetworkShort(idx
);
143 // 1 for the first, etc...
144 const size_t idx
= _offsets
.size() + 1;
147 /// Native objects are handled specially.
151 if (isNativeType(obj
, date
)) {
153 double d
= date
->getTimeValue();
154 #ifdef GNASH_DEBUG_AMF_SERIALIZE
155 log_debug(_("amf: serializing date object "
156 "with index %d and value %g"), idx
, d
);
158 _buf
.appendByte(DATE_AMF0
);
159 writePlainNumber(_buf
, d
);
161 // This should be timezone
162 boost::uint16_t tz
= 0;
163 _buf
.appendNetworkShort(tz
);
168 /// XML is written like a long string (but with an XML marker).
170 if (isNativeType(obj
, xml
)) {
171 _buf
.appendByte(XML_OBJECT_AMF0
);
172 std::ostringstream s
;
173 xml
->toString(s
, true);
175 const std::string
& xmlstr
= s
.str();
176 writePlainString(_buf
, xmlstr
, LONG_STRING_AMF0
);
181 // Any native objects not explicitly handled are unsupported (this
183 _buf
.appendByte(UNSUPPORTED_AMF0
);
187 VM
& vm
= getVM(*obj
);
189 // Arrays are handled specially.
192 const size_t len
= arrayLength(*obj
);
195 // Check if any non-hidden properties are non-numeric.
196 obj
->visitProperties
<IsEnumerable
>(s
);
200 #ifdef GNASH_DEBUG_AMF_SERIALIZE
201 log_debug(_("amf: serializing array of %d "
202 "elements as STRICT_ARRAY (index %d)"),
205 _buf
.appendByte(STRICT_ARRAY_AMF0
);
206 _buf
.appendNetworkLong(len
);
209 for (size_t i
= 0; i
< len
; ++i
) {
210 elem
= getMember(*obj
,arrayKey(vm
, i
));
211 if (!elem
.writeAMF0(*this)) {
212 log_error("Problems serializing strict array "
213 "member %d=%s", i
, elem
);
222 #ifdef GNASH_DEBUG_AMF_SERIALIZE
223 log_debug(_("amf: serializing array of %d "
224 "elements as ECMA_ARRAY (index %d) "),
227 _buf
.appendByte(ECMA_ARRAY_AMF0
);
228 _buf
.appendNetworkLong(len
);
231 // It's a simple object
232 #ifdef GNASH_DEBUG_AMF_SERIALIZE
233 log_debug(_("amf: serializing object (or function) "
234 "with index %d"), idx
);
236 _buf
.appendByte(OBJECT_AMF0
);
239 ObjectSerializer
props(*this, vm
);
240 obj
->visitProperties
<IsEnumerable
>(props
);
241 if (!props
.success()) {
242 log_error("Could not serialize object");
245 _buf
.appendNetworkShort(0);
246 _buf
.appendByte(OBJECT_END_AMF0
);
251 Writer::writeString(const std::string
& str
)
258 Writer::writeNumber(double d
)
265 Writer::writeBoolean(bool b
)
273 Writer::writeUndefined()
275 #ifdef GNASH_DEBUG_AMF_SERIALIZE
276 log_debug(_("amf: serializing undefined"));
278 _buf
.appendByte(UNDEFINED_AMF0
);
285 #ifdef GNASH_DEBUG_AMF_SERIALIZE
286 log_debug(_("amf: serializing null"));
288 _buf
.appendByte(NULL_AMF0
);
293 Writer::writeData(const boost::uint8_t* data
, size_t length
)
295 _buf
.append(data
, length
);
299 Reader::operator()(as_value
& val
, Type t
)
302 // No more reads possible.
307 // This may leave the read position at the _end of the buffer, but
308 // some types are complete with the type byte (null, undefined).
310 t
= static_cast<Type
>(*_pos
);
319 log_error("Unknown AMF type %s! Cannot proceed", t
);
320 // A fatal error, since we don't know how much to parse
325 val
= readBoolean(_pos
, _end
);
329 val
= readString(_pos
, _end
);
332 case LONG_STRING_AMF0
:
333 val
= readLongString(_pos
, _end
);
337 val
= readNumber(_pos
, _end
);
340 case UNSUPPORTED_AMF0
:
346 val
= static_cast<as_object
*>(0);
349 // Object types need access to Global_as to create objects.
351 val
= readReference();
358 case ECMA_ARRAY_AMF0
:
362 case STRICT_ARRAY_AMF0
:
363 val
= readStrictArray();
370 case XML_OBJECT_AMF0
:
375 catch (const AMFException
& e
) {
376 log_error("AMF parsing error: %s", e
.what());
382 /// Construct an XML object.
384 /// Note that the pp seems not to call the constructor or parseXML, but
385 /// rather to create it magically. It could do this by calling an ASNative
390 as_value str
= readLongString(_pos
, _end
);
391 as_function
* ctor
= getMember(_global
, NSV::CLASS_XML
).to_function();
397 VM
& vm
= getVM(_global
);
398 xml
= constructInstance(*ctor
, as_environment(vm
), args
);
404 Reader::readStrictArray()
406 if (_end
- _pos
< 4) {
407 throw AMFException("Read past _end of buffer for strict array length");
410 const boost::uint32_t li
= readNetworkLong(_pos
);
413 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
414 log_debug("amf0 starting read of STRICT_ARRAY with %i elements", li
);
417 as_object
* array
= _global
.createArray();
418 _objectRefs
.push_back(array
);
420 as_value arrayElement
;
421 for (size_t i
= 0; i
< li
; ++i
) {
424 if (!operator()(arrayElement
)) {
425 throw AMFException("Unable to read array elements");
428 callMethod(array
, NSV::PROP_PUSH
, arrayElement
);
431 return as_value(array
);
434 // TODO: this function is inconsistent about when it interrupts parsing
435 // if the AMF is truncated. If it doesn't interrupt, the next read will
441 if (_end
- _pos
< 4) {
442 throw AMFException("Read past _end of buffer for array length");
445 const boost::uint32_t li
= readNetworkLong(_pos
);
448 as_object
* array
= _global
.createArray();
449 _objectRefs
.push_back(array
);
451 // the count specifies array size, so to have that even if none
452 // of the members are indexed
453 // if short, will be incremented everytime an indexed member is
455 array
->set_member(NSV::PROP_LENGTH
, li
);
457 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
458 log_debug("amf0 starting read of ECMA_ARRAY with %i elements", li
);
461 as_value objectElement
;
462 VM
& vm
= getVM(_global
);
465 // It seems we don't mind about this situation, although it means
466 // the next read will fail.
467 if (_end
- _pos
< 2) {
468 log_error("MALFORMED AMF: premature _end of ECMA_ARRAY "
472 const boost::uint16_t strlen
= readNetworkShort(_pos
);
475 // _end of ECMA_ARRAY is signalled by an empty string
476 // followed by an OBJECT_END_AMF0 (0x09) byte
478 // expect an object terminator here
479 if (*_pos
!= OBJECT_END_AMF0
) {
480 log_error("MALFORMED AMF: empty member name not "
481 "followed by OBJECT_END_AMF0 byte");
487 // Throw exception instead?
488 if (_end
- _pos
< strlen
) {
489 log_error("MALFORMED AMF: premature _end of ECMA_ARRAY "
494 const std::string
name(reinterpret_cast<const char*>(_pos
), strlen
);
496 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
497 log_debug("amf0 ECMA_ARRAY prop name is %s", name
);
502 // Recurse to read element.
503 if (!operator()(objectElement
)) {
504 throw AMFException("Unable to read array element");
506 array
->set_member(getURI(vm
, name
), objectElement
);
508 return as_value(array
);
514 VM
& vm
= getVM(_global
);
515 as_object
* obj
= createObject(_global
);
517 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
518 log_debug("amf0 starting read of OBJECT");
521 _objectRefs
.push_back(obj
);
524 std::string keyString
;
527 if (!operator()(tmp
, STRING_AMF0
)) {
528 throw AMFException("Could not read object property name");
530 keyString
= tmp
.to_string();
532 if (keyString
.empty()) {
534 // AMF0 has a redundant "object _end" byte
538 // What is the point?
539 log_error("AMF buffer terminated just before "
540 "object _end byte. continuing anyway.");
542 return as_value(obj
);
545 if (!operator()(tmp
)) {
546 throw AMFException("Unable to read object member");
548 obj
->set_member(getURI(vm
, keyString
), tmp
);
553 Reader::readReference()
556 if (_end
- _pos
< 2) {
557 throw AMFException("Read past _end of buffer for reference index");
559 const boost::uint16_t si
= readNetworkShort(_pos
);
562 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
563 log_debug("readAMF0: reference #%d", si
);
565 if (si
< 1 || si
> _objectRefs
.size()) {
566 log_error("readAMF0: invalid reference to object %d (%d known "
567 "objects)", si
, _objectRefs
.size());
568 throw AMFException("Reference to invalid object reference");
570 return as_value(_objectRefs
[si
- 1]);
576 const double d
= readNumber(_pos
, _end
);
578 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
579 log_debug("amf0 read date: %e", dub
);
582 as_function
* ctor
= getMember(_global
, NSV::CLASS_DATE
).to_function();
583 VM
& vm
= getVM(_global
);
589 date
= constructInstance(*ctor
, as_environment(vm
), args
);
591 if (_end
- _pos
< 2) {
592 throw AMFException("premature _end of input reading "
593 "timezone from Date type");
595 const boost::uint16_t tz
= readNetworkShort(_pos
);
597 log_error(_("Date type encoded timezone info %1%, even though "
598 "this field should not be used."), tz
);