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
22 #include "SimpleBuffer.h"
24 #include "AMFConverter.h"
25 #include "namedStrings.h"
27 #include "as_object.h"
28 #include "ObjectURI.h"
33 #include "Global_as.h"
35 #include "as_function.h"
37 // Define this macro to make AMF parsing verbose
38 //#define GNASH_DEBUG_AMF_DESERIALIZE 1
40 // Define this macto to make AMF writing verbose
41 // #define GNASH_DEBUG_AMF_SERIALIZE 1
49 /// Class used to serialize properties of an object to a buffer
50 class ObjectSerializer
: public AbstractPropertyVisitor
54 ObjectSerializer(Writer
& w
, VM
& vm
)
57 _st(vm
.getStringTable()),
61 bool success() const { return !_error
; }
63 bool accept(const ObjectURI
& uri
, const as_value
& val
) {
64 if (_error
) return true;
66 // Tested with SharedObject and AMFPHP
67 if (val
.is_function()) {
68 log_debug("AMF0: skip serialization of FUNCTION property");
72 const string_table::key key
= getName(uri
);
74 // Test conducted with AMFPHP:
75 // '__proto__' and 'constructor' members
76 // of an object don't get back from an 'echo-service'.
77 // Dunno if they are not serialized or just not sent back.
78 // A '__constructor__' member gets back, but only if
79 // not a function. Actually no function gets back.
80 if (key
== NSV::PROP_uuPROTOuu
|| key
== NSV::PROP_CONSTRUCTOR
)
82 #ifdef GNASH_DEBUG_AMF_SERIALIZE
83 log_debug(" skip serialization of specially-named property %s",
89 // write property name
90 const std::string
& name
= _st
.value(key
);
92 #ifdef GNASH_DEBUG_AMF_SERIALIZE
93 log_debug(" serializing property %s", name
);
95 _writer
.writePropertyName(name
);
96 if (!val
.writeAMF0(_writer
)) {
97 log_error("Problems serializing an object's member");
114 Writer::writePropertyName(const std::string
& name
)
116 writePlainString(_buf
, name
, STRING_AMF0
);
121 Writer::writeObject(as_object
* obj
)
125 // This probably shouldn't happen.
126 if (obj
->to_function()) return false;
128 OffsetTable::iterator it
= _offsets
.find(obj
);
130 // Handle references first.
131 if (it
!= _offsets
.end()) {
132 const size_t idx
= it
->second
;
133 #ifdef GNASH_DEBUG_AMF_SERIALIZE
134 log_debug(_("amf: serializing object (or function) "
135 "as reference to %d"), idx
);
137 _buf
.appendByte(REFERENCE_AMF0
);
138 _buf
.appendNetworkShort(idx
);
142 // 1 for the first, etc...
143 const size_t idx
= _offsets
.size() + 1;
146 /// Native objects are handled specially.
150 if (isNativeType(obj
, date
))
152 double d
= date
->getTimeValue();
153 #ifdef GNASH_DEBUG_AMF_SERIALIZE
154 log_debug(_("amf: serializing date object "
155 "with index %d and value %g"), idx
, d
);
157 _buf
.appendByte(DATE_AMF0
);
159 // This actually only swaps on little-endian machines
163 // This should be timezone
164 boost::uint16_t tz
= 0;
165 _buf
.appendNetworkShort(tz
);
170 /// XML is written like a long string (but with an XML marker).
172 if (isNativeType(obj
, xml
)) {
173 _buf
.appendByte(XML_OBJECT_AMF0
);
174 std::ostringstream s
;
175 xml
->toString(s
, true);
177 const std::string
& xmlstr
= s
.str();
178 writePlainString(_buf
, xmlstr
, LONG_STRING_AMF0
);
183 // Any native objects not explicitly handled are unsupported (this
185 _buf
.appendByte(UNSUPPORTED_AMF0
);
189 VM
& vm
= getVM(*obj
);
191 // Arrays are handled specially.
194 string_table
& st
= vm
.getStringTable();
195 const size_t len
= arrayLength(*obj
);
198 // Check if any non-hidden properties are non-numeric.
199 obj
->visitProperties
<IsEnumerable
>(s
);
203 #ifdef GNASH_DEBUG_AMF_SERIALIZE
204 log_debug(_("amf: serializing array of %d "
205 "elements as STRICT_ARRAY (index %d)"),
208 _buf
.appendByte(STRICT_ARRAY_AMF0
);
209 _buf
.appendNetworkLong(len
);
212 for (size_t i
= 0; i
< len
; ++i
) {
213 elem
= obj
->getMember(arrayKey(st
, i
));
214 if (!elem
.writeAMF0(*this)) {
215 log_error("Problems serializing strict array "
216 "member %d=%s", i
, elem
);
225 #ifdef GNASH_DEBUG_AMF_SERIALIZE
226 log_debug(_("amf: serializing array of %d "
227 "elements as ECMA_ARRAY (index %d) "
228 "[allowStrict:%d, isStrict:%d]"),
229 len
, idx
, allowStrict
, isStrict
);
231 _buf
.appendByte(ECMA_ARRAY_AMF0
);
232 _buf
.appendNetworkLong(len
);
235 // It's a simple object
236 #ifdef GNASH_DEBUG_AMF_SERIALIZE
237 log_debug(_("amf: serializing object (or function) "
238 "with index %d"), idx
);
240 _buf
.appendByte(OBJECT_AMF0
);
243 ObjectSerializer
props(*this, vm
);
244 obj
->visitProperties
<IsEnumerable
>(props
);
245 if (!props
.success()) {
246 log_error("Could not serialize object");
249 _buf
.appendNetworkShort(0);
250 _buf
.appendByte(OBJECT_END_AMF0
);
255 Writer::writeString(const std::string
& str
)
262 Writer::writeNumber(double d
)
269 Writer::writeBoolean(bool b
)
277 Writer::writeUndefined()
279 #ifdef GNASH_DEBUG_AMF_SERIALIZE
280 log_debug(_("amf: serializing undefined"));
282 _buf
.appendByte(UNDEFINED_AMF0
);
289 #ifdef GNASH_DEBUG_AMF_SERIALIZE
290 log_debug(_("amf: serializing null"));
292 _buf
.appendByte(NULL_AMF0
);
297 Writer::writeData(const boost::uint8_t* data
, size_t length
)
299 _buf
.append(data
, length
);
303 Reader::operator()(as_value
& val
, Type t
)
306 // No more reads possible.
311 // This may leave the read position at the _end of the buffer, but
312 // some types are complete with the type byte (null, undefined).
314 t
= static_cast<Type
>(*_pos
);
323 log_error("Unknown AMF type %s! Cannot proceed", t
);
324 // A fatal error, since we don't know how much to parse
329 val
= readBoolean(_pos
, _end
);
333 val
= readString(_pos
, _end
);
336 case LONG_STRING_AMF0
:
337 val
= readLongString(_pos
, _end
);
341 val
= readNumber(_pos
, _end
);
344 case UNSUPPORTED_AMF0
:
350 val
= static_cast<as_object
*>(0);
353 // Object types need access to Global_as to create objects.
355 val
= readReference();
362 case ECMA_ARRAY_AMF0
:
366 case STRICT_ARRAY_AMF0
:
367 val
= readStrictArray();
374 case XML_OBJECT_AMF0
:
379 catch (const AMFException
& e
) {
380 log_error("AMF parsing error: %s", e
.what());
386 /// Construct an XML object.
388 /// Note that the pp seems not to call the constructor or parseXML, but
389 /// rather to create it magically. It could do this by calling an ASNative
394 as_value str
= readLongString(_pos
, _end
);
395 as_function
* ctor
= _global
.getMember(NSV::CLASS_XML
).to_function();
401 VM
& vm
= getVM(_global
);
402 xml
= constructInstance(*ctor
, as_environment(vm
), args
);
408 Reader::readStrictArray()
410 if (_end
- _pos
< 4) {
411 throw AMFException("Read past _end of buffer for strict array length");
414 const boost::uint32_t li
= readNetworkLong(_pos
);
417 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
418 log_debug("amf0 starting read of STRICT_ARRAY with %i elements", li
);
421 as_object
* array
= _global
.createArray();
422 _objectRefs
.push_back(array
);
424 as_value arrayElement
;
425 for (size_t i
= 0; i
< li
; ++i
) {
428 if (!operator()(arrayElement
)) {
429 throw AMFException("Unable to read array elements");
432 callMethod(array
, NSV::PROP_PUSH
, arrayElement
);
435 return as_value(array
);
438 // TODO: this function is inconsistent about when it interrupts parsing
439 // if the AMF is truncated. If it doesn't interrupt, the next read will
445 if (_end
- _pos
< 4) {
446 throw AMFException("Read past _end of buffer for array length");
449 const boost::uint32_t li
= readNetworkLong(_pos
);
452 as_object
* array
= _global
.createArray();
453 _objectRefs
.push_back(array
);
455 // the count specifies array size, so to have that even if none
456 // of the members are indexed
457 // if short, will be incremented everytime an indexed member is
459 array
->set_member(NSV::PROP_LENGTH
, li
);
461 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
462 log_debug("amf0 starting read of ECMA_ARRAY with %i elements", li
);
465 as_value objectElement
;
466 string_table
& st
= getStringTable(_global
);
469 // It seems we don't mind about this situation, although it means
470 // the next read will fail.
471 if (_end
- _pos
< 2) {
472 log_error("MALFORMED AMF: premature _end of ECMA_ARRAY "
476 const boost::uint16_t strlen
= readNetworkShort(_pos
);
479 // _end of ECMA_ARRAY is signalled by an empty string
480 // followed by an OBJECT_END_AMF0 (0x09) byte
482 // expect an object terminator here
483 if (*_pos
!= amf::OBJECT_END_AMF0
) {
484 log_error("MALFORMED AMF: empty member name not "
485 "followed by OBJECT_END_AMF0 byte");
491 // Throw exception instead?
492 if (_end
- _pos
< strlen
) {
493 log_error("MALFORMED AMF: premature _end of ECMA_ARRAY "
498 const std::string
name(reinterpret_cast<const char*>(_pos
), strlen
);
500 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
501 log_debug("amf0 ECMA_ARRAY prop name is %s", name
);
506 // Recurse to read element.
507 if (!operator()(objectElement
)) {
508 throw AMFException("Unable to read array element");
510 array
->set_member(st
.find(name
), objectElement
);
512 return as_value(array
);
519 string_table
& st
= getStringTable(_global
);
520 as_object
* obj
= _global
.createObject();
522 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
523 log_debug("amf0 starting read of OBJECT");
526 _objectRefs
.push_back(obj
);
529 std::string keyString
;
532 if (!operator()(tmp
, amf::STRING_AMF0
)) {
533 throw AMFException("Could not read object property name");
535 keyString
= tmp
.to_string();
537 if (keyString
.empty()) {
539 // AMF0 has a redundant "object _end" byte
543 // What is the point?
544 log_error("AMF buffer terminated just before "
545 "object _end byte. continuing anyway.");
547 return as_value(obj
);
550 if (!operator()(tmp
)) {
551 throw AMFException("Unable to read object member");
553 obj
->set_member(st
.find(keyString
), tmp
);
558 Reader::readReference()
561 if (_end
- _pos
< 2) {
562 throw AMFException("Read past _end of buffer for reference index");
564 const boost::uint16_t si
= readNetworkShort(_pos
);
567 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
568 log_debug("readAMF0: reference #%d", si
);
570 if (si
< 1 || si
> _objectRefs
.size()) {
571 log_error("readAMF0: invalid reference to object %d (%d known "
572 "objects)", si
, _objectRefs
.size());
573 throw AMFException("Reference to invalid object reference");
575 return as_value(_objectRefs
[si
- 1]);
582 if (_end
- _pos
< 8) {
583 throw AMFException("Read past _end of buffer for date type");
588 // TODO: may we avoid a copy and swapBytes call
589 // by bitshifting b[0] trough b[7] ?
590 std::copy(_pos
, _pos
+ 8, reinterpret_cast<char*>(&dub
));
593 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
594 log_debug("amf0 read date: %e", dub
);
597 as_function
* ctor
= _global
.getMember(NSV::CLASS_DATE
).to_function();
598 VM
& vm
= getVM(_global
);
604 date
= constructInstance(*ctor
, as_environment(vm
), args
);
606 if (_end
- _pos
< 2) {
607 throw AMFException("premature _end of input reading "
608 "timezone from Date type");
610 LOG_ONCE(log_unimpl("Timezone info from AMF0 encoded Date object "