drop libxv, add lsb-release
[gnash.git] / libcore / AMFConverter.cpp
blob9aaa727a4e3c71fb0ad1a54c47cae6f4d7bde5b5
1 //
2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
3 // Foundation, Inc
4 //
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.
9 //
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.
14 //
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
20 #include <map>
22 #include "SimpleBuffer.h"
23 #include "AMF.h"
24 #include "AMFConverter.h"
25 #include "namedStrings.h"
26 #include "as_value.h"
27 #include "as_object.h"
28 #include "ObjectURI.h"
29 #include "VM.h"
30 #include "Date_as.h"
31 #include "XML_as.h"
32 #include "Array_as.h"
33 #include "Global_as.h"
34 #include "fn_call.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
43 namespace gnash {
45 namespace amf {
47 namespace {
49 /// Class used to serialize properties of an object to a buffer
50 class ObjectSerializer : public PropertyVisitor
53 public:
54 ObjectSerializer(Writer& w, VM& vm)
56 _writer(w),
57 _st(vm.getStringTable()),
58 _error(false)
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");
69 return true;
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",
84 _st.value(key));
85 #endif
86 return true;
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);
94 #endif
95 _writer.writePropertyName(name);
96 if (!val.writeAMF0(_writer)) {
97 log_error("Problems serializing an object's member");
98 _error = true;
100 return true;
103 private:
105 Writer& _writer;
106 string_table& _st;
107 mutable bool _error;
113 bool
114 Writer::writePropertyName(const std::string& name)
116 writePlainString(_buf, name, STRING_AMF0);
117 return true;
120 bool
121 Writer::writeObject(as_object* obj)
123 assert(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);
136 #endif
137 _buf.appendByte(REFERENCE_AMF0);
138 _buf.appendNetworkShort(idx);
139 return true;
142 // 1 for the first, etc...
143 const size_t idx = _offsets.size() + 1;
144 _offsets[obj] = idx;
146 /// Native objects are handled specially.
147 if (obj->relay()) {
149 Date_as* date;
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);
156 #endif
157 _buf.appendByte(DATE_AMF0);
159 // This actually only swaps on little-endian machines
160 swapBytes(&d, 8);
161 _buf.append(&d, 8);
163 // This should be timezone
164 boost::uint16_t tz = 0;
165 _buf.appendNetworkShort(tz);
167 return true;
170 /// XML is written like a long string (but with an XML marker).
171 XML_as* xml;
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);
180 return true;
183 // Any native objects not explicitly handled are unsupported (this
184 // is just a guess).
185 _buf.appendByte(UNSUPPORTED_AMF0);
186 return true;
189 VM& vm = getVM(*obj);
191 // Arrays are handled specially.
192 if (obj->array()) {
194 string_table& st = vm.getStringTable();
195 const size_t len = arrayLength(*obj);
196 if (_strictArray) {
197 IsStrictArray s(st);
198 // Check if any non-hidden properties are non-numeric.
199 obj->visitProperties<IsEnumerable>(s);
201 if (s.strict()) {
203 #ifdef GNASH_DEBUG_AMF_SERIALIZE
204 log_debug(_("amf: serializing array of %d "
205 "elements as STRICT_ARRAY (index %d)"),
206 len, idx);
207 #endif
208 _buf.appendByte(STRICT_ARRAY_AMF0);
209 _buf.appendNetworkLong(len);
211 as_value elem;
212 for (size_t i = 0; i < len; ++i) {
213 elem = getMember(*obj, arrayKey(st, i));
214 if (!elem.writeAMF0(*this)) {
215 log_error("Problems serializing strict array "
216 "member %d=%s", i, elem);
217 return false;
220 return true;
224 // A normal array.
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);
230 #endif
231 _buf.appendByte(ECMA_ARRAY_AMF0);
232 _buf.appendNetworkLong(len);
234 else {
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);
239 #endif
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");
247 return false;
249 _buf.appendNetworkShort(0);
250 _buf.appendByte(OBJECT_END_AMF0);
251 return true;
254 bool
255 Writer::writeString(const std::string& str)
257 write(_buf, str);
258 return true;
261 bool
262 Writer::writeNumber(double d)
264 write(_buf, d);
265 return true;
268 bool
269 Writer::writeBoolean(bool b)
271 write(_buf, b);
272 return true;
276 bool
277 Writer::writeUndefined()
279 #ifdef GNASH_DEBUG_AMF_SERIALIZE
280 log_debug(_("amf: serializing undefined"));
281 #endif
282 _buf.appendByte(UNDEFINED_AMF0);
283 return true;
286 bool
287 Writer::writeNull()
289 #ifdef GNASH_DEBUG_AMF_SERIALIZE
290 log_debug(_("amf: serializing null"));
291 #endif
292 _buf.appendByte(NULL_AMF0);
293 return true;
296 void
297 Writer::writeData(const boost::uint8_t* data, size_t length)
299 _buf.append(data, length);
302 bool
303 Reader::operator()(as_value& val, Type t)
306 // No more reads possible.
307 if (_pos == _end) {
308 return false;
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).
313 if (t == NOTYPE) {
314 t = static_cast<Type>(*_pos);
315 ++_pos;
318 try {
320 switch (t) {
322 default:
323 log_error("Unknown AMF type %s! Cannot proceed", t);
324 // A fatal error, since we don't know how much to parse
325 return false;
327 // Simple types.
328 case BOOLEAN_AMF0:
329 val = readBoolean(_pos, _end);
330 return true;
332 case STRING_AMF0:
333 val = readString(_pos, _end);
334 return true;
336 case LONG_STRING_AMF0:
337 val = readLongString(_pos, _end);
338 return true;
340 case NUMBER_AMF0:
341 val = readNumber(_pos, _end);
342 return true;
344 case UNSUPPORTED_AMF0:
345 case UNDEFINED_AMF0:
346 val = as_value();
347 return true;
349 case NULL_AMF0:
350 val = static_cast<as_object*>(0);
351 return true;
353 // Object types need access to Global_as to create objects.
354 case REFERENCE_AMF0:
355 val = readReference();
356 return true;
358 case OBJECT_AMF0:
359 val = readObject();
360 return true;
362 case ECMA_ARRAY_AMF0:
363 val = readArray();
364 return true;
366 case STRICT_ARRAY_AMF0:
367 val = readStrictArray();
368 return true;
370 case DATE_AMF0:
371 val = readDate();
372 return true;
374 case XML_OBJECT_AMF0:
375 val = readXML();
376 return true;
379 catch (const AMFException& e) {
380 log_error("AMF parsing error: %s", e.what());
381 return false;
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
390 /// function.
391 as_value
392 Reader::readXML()
394 as_value str = readLongString(_pos, _end);
395 as_function* ctor = getMember(_global, NSV::CLASS_XML).to_function();
397 as_value xml;
398 if (ctor) {
399 fn_call::Args args;
400 args += str;
401 VM& vm = getVM(_global);
402 xml = constructInstance(*ctor, as_environment(vm), args);
404 return xml;
407 as_value
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);
415 _pos += 4;
417 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
418 log_debug("amf0 starting read of STRICT_ARRAY with %i elements", li);
419 #endif
421 as_object* array = _global.createArray();
422 _objectRefs.push_back(array);
424 as_value arrayElement;
425 for (size_t i = 0; i < li; ++i) {
427 // Recurse.
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
440 // fail.
441 as_value
442 Reader::readArray()
445 if (_end - _pos < 4) {
446 throw AMFException("Read past _end of buffer for array length");
449 const boost::uint32_t li = readNetworkLong(_pos);
450 _pos += 4;
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
458 // found
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);
463 #endif
465 as_value objectElement;
466 string_table& st = getStringTable(_global);
467 for (;;) {
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 "
473 "block");
474 break;
476 const boost::uint16_t strlen = readNetworkShort(_pos);
477 _pos += 2;
479 // _end of ECMA_ARRAY is signalled by an empty string
480 // followed by an OBJECT_END_AMF0 (0x09) byte
481 if (!strlen) {
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");
487 ++_pos;
488 break;
491 // Throw exception instead?
492 if (_end - _pos < strlen) {
493 log_error("MALFORMED AMF: premature _end of ECMA_ARRAY "
494 "block");
495 break;
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);
502 #endif
504 _pos += strlen;
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);
515 as_value
516 Reader::readObject()
519 string_table& st = getStringTable(_global);
520 as_object* obj = createObject(_global);
522 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
523 log_debug("amf0 starting read of OBJECT");
524 #endif
526 _objectRefs.push_back(obj);
528 as_value tmp;
529 std::string keyString;
530 for (;;) {
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()) {
538 if (_pos < _end) {
539 // AMF0 has a redundant "object _end" byte
540 ++_pos;
542 else {
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);
557 as_value
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);
565 _pos += 2;
567 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
568 log_debug("readAMF0: reference #%d", si);
569 #endif
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]);
578 as_value
579 Reader::readDate()
582 if (_end - _pos < 8) {
583 throw AMFException("Read past _end of buffer for date type");
586 double dub;
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));
591 _pos += 8;
592 swapBytes(&dub, 8);
593 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
594 log_debug("amf0 read date: %e", dub);
595 #endif
597 as_function* ctor = getMember(_global, NSV::CLASS_DATE).to_function();
598 VM& vm = getVM(_global);
600 as_value date;
601 if (ctor) {
602 fn_call::Args args;
603 args += dub;
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 "
611 "ignored"));
612 _pos += 2;
614 return date;
617 } // namespace amf
618 } // namespace gnash