1 //
2 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
3 //   Free Software 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
18 
19 #include "AMFConverter.h"
20 
21 #include <map>
22 
23 #include "SimpleBuffer.h"
24 #include "AMF.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"
36 #include "PropertyList.h"
37 
38 // Define this macro to make AMF parsing verbose
39 //#define GNASH_DEBUG_AMF_DESERIALIZE 1
40 
41 // Define this macto to make AMF writing verbose
42 // #define GNASH_DEBUG_AMF_SERIALIZE 1
43 
44 namespace gnash {
45 namespace amf {
46 
47 namespace {
48 
49 /// Class used to serialize properties of an object to a buffer
50 class ObjectSerializer : public PropertyVisitor
51 {
52 
53 public:
ObjectSerializer(Writer & w,VM & vm)54     ObjectSerializer(Writer& w, VM& vm)
55         :
56         _writer(w),
57         _st(vm.getStringTable()),
58         _error(false)
59     {}
60 
success() const61     bool success() const { return !_error; }
62 
accept(const ObjectURI & uri,const as_value & val)63     virtual bool accept(const ObjectURI& uri, const as_value& val) {
64 
65         if (_error) return true;
66 
67         // Tested with SharedObject and AMFPHP
68         if (val.is_function()) {
69             log_debug("AMF0: skip serialization of FUNCTION property");
70             return true;
71         }
72 
73         const string_table::key key = getName(uri);
74 
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)
82         {
83 #ifdef GNASH_DEBUG_AMF_SERIALIZE
84             log_debug(" skip serialization of specially-named property %s",
85                     _st.value(key));
86 #endif
87             return true;
88         }
89 
90         // write property name
91         const std::string& name = _st.value(key);
92 
93 #ifdef GNASH_DEBUG_AMF_SERIALIZE
94         log_debug(" serializing property %s", name);
95 #endif
96         _writer.writePropertyName(name);
97         if (!val.writeAMF0(_writer)) {
98             log_error(_("Problems serializing an object's member"));
99             _error = true;
100         }
101         return true;
102     }
103 
104 private:
105 
106     Writer& _writer;
107     string_table& _st;
108     mutable bool _error;
109 
110 };
111 
112 }
113 
114 bool
writePropertyName(const std::string & name)115 Writer::writePropertyName(const std::string& name)
116 {
117     writePlainString(_buf, name, STRING_AMF0);
118     return true;
119 }
120 
121 bool
writeObject(as_object * obj)122 Writer::writeObject(as_object* obj)
123 {
124     assert(obj);
125 
126     // This probably shouldn't happen.
127     if (obj->to_function()) return false;
128 
129     OffsetTable::iterator it = _offsets.find(obj);
130 
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);
137 #endif
138         _buf.appendByte(REFERENCE_AMF0);
139         _buf.appendNetworkShort(idx);
140         return true;
141     }
142 
143     // 1 for the first, etc...
144     const size_t idx = _offsets.size() + 1;
145     _offsets[obj] = idx;
146 
147     /// Native objects are handled specially.
148     if (obj->relay()) {
149 
150         Date_as* date;
151         if (isNativeType(obj, date)) {
152 
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);
157 #endif
158             _buf.appendByte(DATE_AMF0);
159             writePlainNumber(_buf, d);
160 
161             // This should be timezone
162             std::uint16_t tz = 0;
163             _buf.appendNetworkShort(tz);
164 
165             return true;
166         }
167 
168         /// XML is written like a long string (but with an XML marker).
169         XML_as* xml;
170         if (isNativeType(obj, xml)) {
171             _buf.appendByte(XML_OBJECT_AMF0);
172             std::ostringstream s;
173             xml->toString(s, true);
174 
175             const std::string& xmlstr = s.str();
176             writePlainString(_buf, xmlstr, LONG_STRING_AMF0);
177 
178             return true;
179         }
180 
181         // Any native objects not explicitly handled are unsupported (this
182         // is just a guess).
183         _buf.appendByte(UNSUPPORTED_AMF0);
184         return true;
185     }
186 
187     VM& vm = getVM(*obj);
188 
189     // Arrays are handled specially.
190     if (obj->array()) {
191 
192         const size_t len = arrayLength(*obj);
193         if (_strictArray) {
194             IsStrictArray s(vm);
195             // Check if any non-hidden properties are non-numeric.
196             obj->visitProperties<IsEnumerable>(s);
197 
198             if (s.strict()) {
199 
200 #ifdef GNASH_DEBUG_AMF_SERIALIZE
201                 log_debug("amf: serializing array of %d "
202                             "elements as STRICT_ARRAY (index %d)",
203                             len, idx);
204 #endif
205                 _buf.appendByte(STRICT_ARRAY_AMF0);
206                 _buf.appendNetworkLong(len);
207 
208                 as_value elem;
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);
214                         return false;
215                     }
216                 }
217                 return true;
218             }
219         }
220 
221         // A normal array.
222 #ifdef GNASH_DEBUG_AMF_SERIALIZE
223         log_debug("amf: serializing array of %d "
224                     "elements as ECMA_ARRAY (index %d) ",
225                     len, idx);
226 #endif
227         _buf.appendByte(ECMA_ARRAY_AMF0);
228         _buf.appendNetworkLong(len);
229     }
230     else {
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);
235 #endif
236         _buf.appendByte(OBJECT_AMF0);
237     }
238 
239     ObjectSerializer props(*this, vm);
240     obj->visitProperties<IsEnumerable>(props);
241     if (!props.success()) {
242         log_error(_("Could not serialize object"));
243         return false;
244     }
245     _buf.appendNetworkShort(0);
246     _buf.appendByte(OBJECT_END_AMF0);
247     return true;
248 }
249 
250 bool
writeString(const std::string & str)251 Writer::writeString(const std::string& str)
252 {
253     write(_buf, str);
254     return true;
255 }
256 
257 bool
writeNumber(double d)258 Writer::writeNumber(double d)
259 {
260     write(_buf, d);
261     return true;
262 }
263 
264 bool
writeBoolean(bool b)265 Writer::writeBoolean(bool b)
266 {
267     write(_buf, b);
268     return true;
269 }
270 
271 
272 bool
writeUndefined()273 Writer::writeUndefined()
274 {
275 #ifdef GNASH_DEBUG_AMF_SERIALIZE
276     log_debug("amf: serializing undefined");
277 #endif
278     _buf.appendByte(UNDEFINED_AMF0);
279     return true;
280 }
281 
282 bool
writeNull()283 Writer::writeNull()
284 {
285 #ifdef GNASH_DEBUG_AMF_SERIALIZE
286     log_debug("amf: serializing null");
287 #endif
288     _buf.appendByte(NULL_AMF0);
289     return true;
290 }
291 
292 void
writeData(const std::uint8_t * data,size_t length)293 Writer::writeData(const std::uint8_t* data, size_t length)
294 {
295     _buf.append(data, length);
296 }
297 
298 bool
operator ()(as_value & val,Type t)299 Reader::operator()(as_value& val, Type t)
300 {
301 
302     // No more reads possible.
303     if (_pos == _end) {
304         return false;
305     }
306 
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).
309     if (t == NOTYPE) {
310         t = static_cast<Type>(*_pos);
311         ++_pos;
312     }
313 
314     try {
315 
316         switch (t) {
317 
318             default:
319                 log_error(_("Unknown AMF type %s! Cannot proceed"), t);
320                 // A fatal error, since we don't know how much to parse
321                 return false;
322 
323             // Simple types.
324             case BOOLEAN_AMF0:
325                 val = readBoolean(_pos, _end);
326                 return true;
327 
328             case STRING_AMF0:
329                 val = readString(_pos, _end);
330                 return true;
331 
332             case LONG_STRING_AMF0:
333                  val = readLongString(_pos, _end);
334                  return true;
335 
336             case NUMBER_AMF0:
337                 val = readNumber(_pos, _end);
338                 return true;
339 
340             case UNSUPPORTED_AMF0:
341             case UNDEFINED_AMF0:
342                 val = as_value();
343                 return true;
344 
345             case NULL_AMF0:
346                 val = static_cast<as_object*>(nullptr);
347                 return true;
348 
349             // Object types need access to Global_as to create objects.
350             case REFERENCE_AMF0:
351                 val = readReference();
352                 return true;
353 
354             case OBJECT_AMF0:
355                 val = readObject();
356                 return true;
357 
358             case ECMA_ARRAY_AMF0:
359                 val = readArray();
360                 return true;
361 
362             case STRICT_ARRAY_AMF0:
363                 val = readStrictArray();
364                 return true;
365 
366             case DATE_AMF0:
367                 val = readDate();
368                 return true;
369 
370             case XML_OBJECT_AMF0:
371                 val = readXML();
372                 return true;
373         }
374     }
375     catch (const AMFException& e) {
376         log_error(_("AMF parsing error: %s"), e.what());
377         return false;
378     }
379 
380 }
381 
382 /// Construct an XML object.
383 //
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
386 /// function.
387 as_value
readXML()388 Reader::readXML()
389 {
390     as_value str = readLongString(_pos, _end);
391     as_function* ctor = getMember(_global, NSV::CLASS_XML).to_function();
392 
393     as_value xml;
394     if (ctor) {
395         fn_call::Args args;
396         args += str;
397         VM& vm = getVM(_global);
398         xml = constructInstance(*ctor, as_environment(vm), args);
399     }
400     return xml;
401 }
402 
403 as_value
readStrictArray()404 Reader::readStrictArray()
405 {
406     if (_end - _pos < 4) {
407         throw AMFException(_("Read past _end of buffer for strict array length"));
408     }
409 
410     const std::uint32_t li = readNetworkLong(_pos);
411     _pos += 4;
412 
413 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
414     log_debug("amf0 starting read of STRICT_ARRAY with %i elements", li);
415 #endif
416 
417     as_object* array = _global.createArray();
418     _objectRefs.push_back(array);
419 
420     as_value arrayElement;
421     for (size_t i = 0; i < li; ++i) {
422 
423         // Recurse.
424         if (!operator()(arrayElement)) {
425             throw AMFException(_("Unable to read array elements"));
426         }
427 
428         callMethod(array, NSV::PROP_PUSH, arrayElement);
429     }
430 
431     return as_value(array);
432 }
433 
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
436 // fail.
437 as_value
readArray()438 Reader::readArray()
439 {
440 
441     if (_end - _pos < 4) {
442         throw AMFException(_("Read past _end of buffer for array length"));
443     }
444 
445     const std::uint32_t li = readNetworkLong(_pos);
446     _pos += 4;
447 
448     as_object* array = _global.createArray();
449     _objectRefs.push_back(array);
450 
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
454     // found
455     array->set_member(NSV::PROP_LENGTH, li);
456 
457 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
458     log_debug("amf0 starting read of ECMA_ARRAY with %i elements", li);
459 #endif
460 
461     as_value objectElement;
462     VM& vm = getVM(_global);
463     for (;;) {
464 
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 "
469                         "block"));
470             break;
471         }
472         const std::uint16_t strlen = readNetworkShort(_pos);
473         _pos += 2;
474 
475         // _end of ECMA_ARRAY is signalled by an empty string
476         // followed by an OBJECT_END_AMF0 (0x09) byte
477         if (!strlen) {
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"));
482             }
483             ++_pos;
484             break;
485         }
486 
487         // Throw exception instead?
488         if (_end - _pos < strlen) {
489             log_error(_("MALFORMED AMF: premature _end of ECMA_ARRAY "
490                       "block"));
491             break;
492         }
493 
494         const std::string name(reinterpret_cast<const char*>(_pos), strlen);
495 
496 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
497         log_debug("amf0 ECMA_ARRAY prop name is %s", name);
498 #endif
499 
500         _pos += strlen;
501 
502         // Recurse to read element.
503         if (!operator()(objectElement)) {
504             throw AMFException(_("Unable to read array element"));
505         }
506         array->set_member(getURI(vm, name), objectElement);
507     }
508     return as_value(array);
509 }
510 
511 as_value
readObject()512 Reader::readObject()
513 {
514     VM& vm = getVM(_global);
515     as_object* obj = createObject(_global);
516 
517 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
518     log_debug("amf0 starting read of OBJECT");
519 #endif
520 
521     _objectRefs.push_back(obj);
522 
523     as_value tmp;
524     std::string keyString;
525     for (;;) {
526 
527         if (!operator()(tmp, STRING_AMF0)) {
528             throw AMFException(_("Could not read object property name"));
529         }
530         keyString = tmp.to_string();
531 
532         if (keyString.empty()) {
533             if (_pos < _end) {
534                 // AMF0 has a redundant "object _end" byte
535                 ++_pos;
536             }
537             else {
538                 // What is the point?
539                 log_error(_("AMF buffer terminated just before "
540                             "object _end byte. continuing anyway."));
541             }
542             return as_value(obj);
543         }
544 
545         if (!operator()(tmp)) {
546             throw AMFException("Unable to read object member");
547         }
548         obj->set_member(getURI(vm, keyString), tmp);
549     }
550 }
551 
552 as_value
readReference()553 Reader::readReference()
554 {
555 
556     if (_end - _pos < 2) {
557         throw AMFException("Read past _end of buffer for reference index");
558     }
559     const std::uint16_t si = readNetworkShort(_pos);
560     _pos += 2;
561 
562 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
563     log_debug("readAMF0: reference #%d", si);
564 #endif
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");
569     }
570     return as_value(_objectRefs[si - 1]);
571 }
572 
573 as_value
readDate()574 Reader::readDate()
575 {
576     const double d = readNumber(_pos, _end);
577 
578 #ifdef GNASH_DEBUG_AMF_DESERIALIZE
579     log_debug("amf0 read date: %e", dub);
580 #endif
581 
582     as_function* ctor = getMember(_global, NSV::CLASS_DATE).to_function();
583     VM& vm = getVM(_global);
584 
585     as_value date;
586     if (ctor) {
587         fn_call::Args args;
588         args += d;
589         date = constructInstance(*ctor, as_environment(vm), args);
590 
591         if (_end - _pos < 2) {
592             throw AMFException("premature _end of input reading "
593                         "timezone from Date type");
594         }
595         const std::uint16_t tz = readNetworkShort(_pos);
596         if (tz != 0) {
597             log_error(_("Date type encoded timezone info %1%, even though "
598                 "this field should not be used."), tz);
599         }
600         _pos += 2;
601     }
602     return date;
603 }
604 
605 } // namespace amf
606 } // namespace gnash
607