1 // amf.cpp:  AMF (Action Message Format) rpc marshalling, for Gnash.
2 //
3 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 //   Free Software Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 //
20 
21 #include "GnashSystemNetHeaders.h"
22 #include "log.h"
23 #include "GnashException.h"
24 #include "buffer.h"
25 #include "amf.h"
26 #include "element.h"
27 #include "amfutf8.h"
28 
29 #include <string>
30 #include <vector>
31 #include <map>
32 #include <cstdint>
33 
34 namespace cygnal
35 {
36 
37 /// \define ENSUREBYTES
38 ///
39 /// @param from The base address to check.
40 ///
41 /// @param tooFar The ending address that is one byte too many.
42 ///
43 /// @param size The number of bytes to check for: from to tooFar.
44 ///
45 /// @remarks May throw an Exception
46 #define ENSUREBYTES(from, toofar, size) { \
47 	if ( from+size >= toofar ) \
48 		throw ParserException("Premature end of AMF stream"); \
49 }
50 
51 
52 /// \brief String representations of AMF0 data types.
53 ///
54 ///	These are used to print more intelligent debug messages.
55 const char *amftype_str[] = {
56     "Number",
57     "Boolean",
58     "String",
59     "Object",
60     "MovieClip",
61     "Null",
62     "Undefined",
63     "Reference",
64     "ECMAArray",
65     "ObjectEnd",
66     "StrictArray",
67     "Date",
68     "LongString",
69     "Unsupported",
70     "Recordset",
71     "XMLObject",
72     "TypedObject",
73     "AMF3 Data"
74 };
75 
76 /// \brief Create a new AMF class.
77 ///	As most of the methods in the AMF class a static, this
78 ///	is primarily only used when encoding complex objects
79 ///	where the byte count is accumulated.
AMF()80 AMF::AMF()
81     : _totalsize(0)
82 {
83 //    GNASH_REPORT_FUNCTION;
84 }
85 
86 /// Delete the alloczted AMF class
~AMF()87 AMF::~AMF()
88 {
89 //    GNASH_REPORT_FUNCTION;
90 }
91 
92 /// \brief Swap bytes in raw data.
93 ///	This only swaps bytes if the host byte order is little endian.
94 ///
95 /// @param word The address of the data to byte swap.
96 ///
97 /// @param size The number of bytes in the data.
98 ///
99 /// @return A pointer to the raw data.
100 void *
swapBytes(void * word,size_t size)101 swapBytes(void *word, size_t size)
102 {
103     union {
104 	std::uint16_t s;
105 	struct {
106 	    std::uint8_t c0;
107 	    std::uint8_t c1;
108 	} c;
109     } u;
110 
111     u.s = 1;
112     if (u.c.c0 == 0) {
113 	// Big-endian machine: do nothing
114 	return word;
115     }
116 
117     // Little-endian machine: byte-swap the word
118 
119     // A conveniently-typed pointer to the source data
120     std::uint8_t *x = static_cast<std::uint8_t *>(word);
121 
122     /// Handle odd as well as even counts of bytes
123     std::reverse(x, x+size);
124 
125     return word;
126 }
127 
128 //
129 // Methods for encoding data into big endian formatted raw AMF data.
130 //
131 
132 /// \brief Encode a 64 bit number to it's serialized representation.
133 ///
134 /// @param num A double value to serialize.
135 ///
136 /// @return a binary AMF packet in big endian format
137 std::shared_ptr<Buffer>
encodeNumber(double indata)138 AMF::encodeNumber(double indata)
139 {
140 //    GNASH_REPORT_FUNCTION;
141     double num;
142     // Encode the data as a 64 bit, big-endian, numeric value
143     // only one additional byte for the type
144     std::shared_ptr<Buffer> buf(new Buffer(AMF0_NUMBER_SIZE + 1));
145     *buf = Element::NUMBER_AMF0;
146     num = indata;
147     swapBytes(&num, AMF0_NUMBER_SIZE);
148     *buf += num;
149 
150     return buf;
151 }
152 
153 /// \brief Encode a Boolean object to it's serialized representation.
154 ///
155 /// @param flag The boolean value to serialize.
156 ///
157 /// @return a binary AMF packet in big endian format
158 std::shared_ptr<Buffer>
encodeBoolean(bool flag)159 AMF::encodeBoolean(bool flag)
160 {
161 //    GNASH_REPORT_FUNCTION;
162     // Encode a boolean value. 0 for false, 1 for true
163     std::shared_ptr<Buffer> buf(new Buffer(2));
164     *buf = Element::BOOLEAN_AMF0;
165     *buf += static_cast<std::uint8_t>(flag);
166 
167     return buf;
168 }
169 
170 /// \brief Encode the end of an object to it's serialized representation.
171 ///
172 /// @return a binary AMF packet in big endian format
173 std::shared_ptr<Buffer>
encodeObject(const cygnal::Element & data)174 AMF::encodeObject(const cygnal::Element &data)
175 {
176 //    GNASH_REPORT_FUNCTION;
177     std::uint32_t length;
178     length = data.propertySize();
179     gnash::log_debug(_("Encoded data size has %d properties"), length);
180     std::shared_ptr<cygnal::Buffer> buf;
181     if (length) {
182 	buf.reset(new cygnal::Buffer);
183     } else {
184 	return buf;
185     }
186 
187     *buf = Element::OBJECT_AMF0;
188     if (data.propertySize() > 0) {
189 	std::vector<std::shared_ptr<cygnal::Element> >::const_iterator ait;
190 	std::vector<std::shared_ptr<cygnal::Element> > props = data.getProperties();
191 	for (ait = props.begin(); ait != props.end(); ++ait) {
192 	    std::shared_ptr<cygnal::Element> el = (*(ait));
193 	    std::shared_ptr<cygnal::Buffer> item = AMF::encodeElement(el);
194 	    if (item) {
195 		*buf += item;
196 		item.reset();
197 	    } else {
198 		break;
199 	    }
200 	    //	    el->dump();
201 	}
202     }
203 
204     // Terminate the object
205     *buf += '\0';
206     *buf += '\0';
207     *buf += TERMINATOR;
208 
209     return buf;
210 }
211 
212 /// \brief Encode the end of an object to it's serialized representation.
213 ///
214 /// @return a binary AMF packet in big endian format
215 std::shared_ptr<Buffer>
encodeObjectEnd()216 AMF::encodeObjectEnd()
217 {
218 //    GNASH_REPORT_FUNCTION;
219     std::shared_ptr<Buffer> buf(new Buffer(1));
220     *buf += TERMINATOR;
221 
222     return buf;
223 }
224 
225 /// \brief Encode an "Undefined" object to it's serialized representation.
226 ///
227 /// @return a binary AMF packet in big endian format
228 std::shared_ptr<Buffer>
encodeUndefined()229 AMF::encodeUndefined()
230 {
231 //    GNASH_REPORT_FUNCTION;
232     std::shared_ptr<Buffer> buf(new Buffer(1));
233     *buf = Element::UNDEFINED_AMF0;
234 
235     return buf;
236 }
237 
238 /// \brief Encode a "Unsupported" object to it's serialized representation.
239 ///
240 /// @return a binary AMF packet in big endian format
241 std::shared_ptr<Buffer>
encodeUnsupported()242 AMF::encodeUnsupported()
243 {
244 //    GNASH_REPORT_FUNCTION;
245     std::shared_ptr<Buffer> buf(new Buffer(1));
246     *buf = Element::UNSUPPORTED_AMF0;
247 
248     return buf;
249 }
250 
251 /// \brief Encode a Date to it's serialized representation.
252 ///
253 /// @param data A pointer to the raw bytes that becomes the data.
254 ///
255 /// @return a binary AMF packet in big endian format
256 std::shared_ptr<Buffer>
encodeDate(const std::uint8_t * date)257 AMF::encodeDate(const std::uint8_t *date)
258 {
259 //    GNASH_REPORT_FUNCTION;
260 //    std::shared_ptr<Buffer> buf;
261     std::shared_ptr<Buffer> buf;
262     if (date != nullptr) {
263 	buf.reset(new Buffer(AMF0_NUMBER_SIZE+1));
264 	*buf = Element::DATE_AMF0;
265 	double num = *(reinterpret_cast<const double*>(date));
266 	swapBytes(&num, AMF0_NUMBER_SIZE);
267 	*buf += num;
268     }
269     return buf;
270 }
271 
272 /// \brief Encode a NULL object to it's serialized representation.
273 ///		A NULL object is often used as a placeholder in RTMP.
274 ///
275 /// @return a binary AMF packet in big endian format
276 std::shared_ptr<Buffer>
encodeNull()277 AMF::encodeNull()
278 {
279 //    GNASH_REPORT_FUNCTION;
280 
281     std::shared_ptr<Buffer> buf(new Buffer(1));
282     *buf = Element::NULL_AMF0;
283 
284     return buf;
285 }
286 
287 /// \brief Encode an XML object to it's serialized representation.
288 ///
289 /// @param data A pointer to the raw bytes that becomes the XML data.
290 ///
291 /// @param nbytes The number of bytes to serialize.
292 ///
293 /// @return a binary AMF packet in big endian format
294 std::shared_ptr<Buffer>
encodeXMLObject(const std::uint8_t *,size_t)295 AMF::encodeXMLObject(const std::uint8_t * /*data */, size_t /* size */)
296 {
297 //    GNASH_REPORT_FUNCTION;
298     std::shared_ptr<Buffer> buf;
299     gnash::log_unimpl(_("XML AMF objects not supported yet"));
300     buf.reset();
301     return buf;
302 }
303 
304 /// \brief Encode a Typed Object to it's serialized representation.
305 ///
306 /// @param data A pointer to the raw bytes that becomes the data.
307 ///
308 /// @param size The number of bytes to serialize.
309 ///
310 /// @return a binary AMF packet in big endian format
311 std::shared_ptr<Buffer>
encodeTypedObject(const cygnal::Element & data)312 AMF::encodeTypedObject(const cygnal::Element &data)
313 {
314 //    GNASH_REPORT_FUNCTION;
315 
316     size_t size = 0;
317     std::uint32_t props;
318     props = data.propertySize();
319     std::shared_ptr<cygnal::Buffer> buf;
320     //    log_debug("Encoded data size has %d properties", props);
321     if (props) {
322 	// Calculate the total size of the output buffer
323 	// needed to hold the encoded properties
324 	for (size_t i=0; i<data.propertySize(); i++) {
325 	    size += data.getProperty(i)->getDataSize();
326 	    size += data.getProperty(i)->getNameSize();
327 	    size += AMF_PROP_HEADER_SIZE;
328 	}
329 	size += data.getNameSize();
330 	buf.reset(new Buffer(size+24)); // FIXME: why are we several words off ?
331     }
332 
333     *buf = Element::TYPED_OBJECT_AMF0;
334 
335     size_t length = data.getNameSize();
336     std::uint16_t enclength = length;
337     swapBytes(&enclength, 2);
338     *buf += enclength;
339 
340     if (data.getName()) {
341 	std::string name = data.getName();
342 	if (name.size() > 0) {
343 	    *buf += name;
344 	}
345     }
346 
347     if (data.propertySize() > 0) {
348 	std::vector<std::shared_ptr<cygnal::Element> >::const_iterator ait;
349 	std::vector<std::shared_ptr<cygnal::Element> > props = data.getProperties();
350 	for (ait = props.begin(); ait != props.end(); ++ait) {
351 	    std::shared_ptr<cygnal::Element> el = (*(ait));
352 	    std::shared_ptr<cygnal::Buffer> item = AMF::encodeElement(el);
353 	    if (item) {
354 		*buf += item;
355 		item.reset();
356 	    } else {
357 		break;
358 	    }
359 	    //	    el->dump();
360 	}
361     }
362 
363     // Terminate the object
364     *buf += '\0';
365     *buf += '\0';
366     *buf += TERMINATOR;
367 
368     return buf;
369 }
370 
371 /// \brief Encode a Reference to an object to it's serialized representation.
372 ///
373 /// @param data A pointer to the raw bytes that becomes the data.
374 ///
375 /// @param size The number of bytes to serialize.
376 ///
377 /// @return a binary AMF packet in big endian format (header,data)
378 std::shared_ptr<Buffer>
encodeReference(std::uint16_t index)379 AMF::encodeReference(std::uint16_t index)
380 {
381 //    GNASH_REPORT_FUNCTION;
382     std::uint16_t num = index;
383     std::shared_ptr<cygnal::Buffer> buf(new Buffer(3));
384     *buf = Element::REFERENCE_AMF0;
385     swapBytes(&num, sizeof(std::uint16_t));
386     *buf += num;
387 
388     return buf;
389 }
390 
391 /// \brief Encode a Movie Clip (swf data) to it's serialized representation.
392 ///
393 /// @param data A pointer to the raw bytes that becomes the data.
394 ///
395 /// @param size The number of bytes to serialize.
396 ///
397 /// @return a binary AMF packet in big endian format (header,data)
398 std::shared_ptr<Buffer>
encodeMovieClip(const std::uint8_t *,size_t)399 AMF::encodeMovieClip(const std::uint8_t * /*data */, size_t /* size */)
400 {
401 //    GNASH_REPORT_FUNCTION;
402     std::shared_ptr<Buffer> buf;
403     gnash::log_unimpl(_("Movie Clip AMF objects not supported yet"));
404 
405     return buf;
406 }
407 
408 /// \brief Encode an ECMA Array to it's serialized representation.
409 ///		An ECMA Array, also called a Mixed Array, contains any
410 ///		AMF data type as an item in the array.
411 ///
412 /// @param data A pointer to the raw bytes that becomes the data.
413 ///
414 /// @param size The number of bytes to serialize.
415 ///
416 /// @return a binary AMF packet in big endian format
417 std::shared_ptr<Buffer>
encodeECMAArray(const cygnal::Element & data)418 AMF::encodeECMAArray(const cygnal::Element &data)
419 {
420 //    GNASH_REPORT_FUNCTION;
421     std::uint32_t length;
422     bool sparse = false;
423     //size_t counter = 0;
424 
425     length = data.propertySize();
426     //    log_debug("Encoded data size has %d properties", length);
427     std::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer);
428     if (length == 0) {
429 	// an undefined array is only 5 bytes, 1 for the type and
430 	// 4 for the length.
431 	buf.reset(new cygnal::Buffer(5));
432     }
433     *buf = Element::ECMA_ARRAY_AMF0;
434     length = 0;
435     swapBytes(&length, sizeof(std::uint32_t));
436     *buf += length;
437 
438     // At lest for red5, it seems to encode from the last item to the
439     // first, so we do the same for now.
440     if (data.propertySize() > 0) {
441 	std::shared_ptr<cygnal::Buffer> item;
442 	std::vector<std::shared_ptr<cygnal::Element> >::const_iterator ait;
443 	std::vector<std::shared_ptr<cygnal::Element> > props = data.getProperties();
444 	for (ait = props.begin(); ait != props.end(); ++ait) {
445 	    std::shared_ptr<cygnal::Element> el = (*(ait));
446 	    if (sparse) {
447 		sparse = false;
448 // 		char num[12];
449 // 		sprintf(num, "%d", counter);
450 // 		cygnal::Element elnum(num, el->to_number());
451 // 		*buf += AMF::encodeElement(elnum);
452 // 		double nodes = items;
453  		cygnal::Element ellen("length");
454 		ellen.makeNumber(data.propertySize());
455  		*buf += AMF::encodeElement(ellen);
456 	    } else {
457 		item = AMF::encodeElement(el);
458 		if (item) {
459 		    *buf += item;
460 		    item.reset();
461 		} else {
462 		    break;
463 		}
464 	    }
465 	}
466     }
467 #if 0
468     double count = data.propertySize();
469     cygnal::Element ellen("length", count);
470     std::shared_ptr<cygnal::Buffer> buflen = ellen.encode();
471     *buf += buflen;
472 #endif
473 
474     // Terminate the object
475     *buf += '\0';
476     *buf += '\0';
477     *buf += TERMINATOR;
478 
479     return buf;
480 }
481 
482 /// \brief Encode a Long String to it's serialized representation.
483 ///
484 /// @param data A pointer to the raw bytes that becomes the data.
485 ///
486 /// @param size The number of bytes to serialize.
487 ///
488 /// @return a binary AMF packet in big endian format
489 std::shared_ptr<Buffer>
encodeLongString(const std::uint8_t *,size_t)490 AMF::encodeLongString(const std::uint8_t * /* data */, size_t /* size */)
491 {
492 //    GNASH_REPORT_FUNCTION;
493     std::shared_ptr<Buffer> buf;
494     gnash::log_unimpl(_("Long String AMF objects not supported yet"));
495 
496     return buf;
497 }
498 
499 /// \brief Encode a Record Set to it's serialized representation.
500 ///
501 /// @param data A pointer to the raw bytes that becomes the data.
502 ///
503 /// @param size The number of bytes to serialize.
504 ///
505 /// @return a binary AMF packet in big endian format
506 std::shared_ptr<Buffer>
encodeRecordSet(const std::uint8_t *,size_t)507 AMF::encodeRecordSet(const std::uint8_t * /* data */, size_t /* size */)
508 {
509 //    GNASH_REPORT_FUNCTION;
510     std::shared_ptr<Buffer> buf;
511     gnash::log_unimpl(_("Reecord Set AMF objects not supported yet"));
512 
513     return buf;
514 }
515 
516 /// \brief Encode a Strict Array to it's serialized represenicttation.
517 ///	A Strict Array is one where all the items are the same
518 ///	data type, commonly either a number or a string.
519 ///
520 /// @param data A pointer to the raw bytes that becomes the data.
521 ///
522 /// @param size The number of bytes to serialize.
523 ///
524 /// @return a binary AMF packet in big endian format (header,data)
525 std::shared_ptr<Buffer>
encodeStrictArray(const cygnal::Element & data)526 AMF::encodeStrictArray(const cygnal::Element &data)
527 {
528 //    GNASH_REPORT_FUNCTION;
529     std::uint32_t items;
530     items = data.propertySize();
531     //    log_debug("Encoded data size has %d properties", items);
532     std::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer);
533     if (items) {
534 	buf.reset(new cygnal::Buffer);
535     } else {
536 	// an undefined array is only 5 bytes, 1 for the type and
537 	// 4 for the length.
538 	buf->resize(5);
539 	//	buf.reset(new cygnal::Buffer(5));
540     }
541     *buf = Element::STRICT_ARRAY_AMF0;
542     swapBytes(&items, sizeof(std::uint32_t));
543     *buf += items;
544 
545     if (data.propertySize() > 0) {
546 	std::vector<std::shared_ptr<cygnal::Element> >::const_iterator ait;
547 	std::vector<std::shared_ptr<cygnal::Element> > props = data.getProperties();
548 	bool sparse = false;
549 	size_t counter = 0;
550 	for (ait = props.begin(); ait != props.end(); ++ait) {
551 	    counter++;
552 	    std::shared_ptr<cygnal::Element> el = (*(ait));
553 #if 0
554 	    // FIXME: Red5's echo tests like to turn strict array's into ecma
555 	    // arrays, but we shouldn't do that in the core.
556 	    // If we see an undefined data item, then switch to an ECMA
557 	    // array which is more compact. At least this is what Red5 does.
558 	    if (el->getType() == Element::UNDEFINED_AMF0) {
559 		if (!sparse) {
560 		    gnash::log_debug(_("Encoding a strict array as an ecma array"));
561 		    *buf->reference() = Element::ECMA_ARRAY_AMF0;
562 		    // When returning an ECMA array for a sparsely populated
563 		    // array, Red5 adds one more to the count to be 1 based,
564 		    // instead of zero based.
565 		    std::uint32_t moreitems = data.propertySize() + 1;
566 		    swapBytes(&moreitems, sizeof(std::uint32_t));
567 		    std::uint8_t *ptr = buf->reference() + 1;
568 		    memcpy(ptr, &moreitems, sizeof(std::uint32_t));
569 		    sparse = true;
570 		}
571 		continue;
572 	    } else {
573 #endif
574 		if (sparse) {
575 		    sparse = false;
576 		    std::ostringstream os;
577 		    os << counter;
578 		    cygnal::Element elnum(os.str().c_str(), el->to_number());
579 		    *buf += AMF::encodeElement(elnum);
580 		    double nodes = items;
581 		    cygnal::Element ellen("length", nodes);
582 		    *buf += AMF::encodeElement(ellen);
583 		} else {
584 		    std::shared_ptr<cygnal::Buffer> item = AMF::encodeElement(el);
585 		    if (item) {
586 			*buf += item;
587 			item.reset();
588 			continue;
589 		    } else {
590 			break;
591 		    }
592 		}
593 //	    }
594 // 	    el->dump();
595 	}
596     }
597 
598     return buf;
599 }
600 
601 /// \brief Encode a string to it's serialized representation.
602 ///
603 /// @param str a string value
604 ///
605 /// @return a binary AMF packet in big endian format
606 std::shared_ptr<Buffer>
607 AMF::encodeString(const std::string &str)
608 {
609     std::uint8_t *ptr = const_cast<std::uint8_t *>(reinterpret_cast<const std::uint8_t *>(str.c_str()));
610     return encodeString(ptr, str.size());
611 }
612 
613 /// \brief Encode a string to it's serialized representation.
614 ///
615 /// @param data The data to serialize into big endian format
616 ///
617 /// @param size The size of the data in bytes
618 ///
619 /// @return a binary AMF packet in big endian format
620 std::shared_ptr<Buffer>
621 AMF::encodeString(std::uint8_t *data, size_t size)
622 {
623 //    GNASH_REPORT_FUNCTION;
624     std::shared_ptr<Buffer>buf(new Buffer(size + AMF_HEADER_SIZE));
625     *buf = Element::STRING_AMF0;
626     // when a string is stored in an element, we add a NULL terminator so
627     // it can be printed by to_string() efficiently. The NULL terminator
628     // doesn't get written when encoding a string as it has a byte count
629     // instead.
630     std::uint16_t length = size;
631 //    log_debug("Encoded data size is going to be %d", length);
632     swapBytes(&length, 2);
633     *buf += length;
634     buf->append(data, size);
635 
636     return buf;
637 }
638 
639 /// \brief Encode a String object to it's serialized representation.
640 ///	A NULL String is a string with no associated data.
641 ///
642 /// @return a binary AMF packet in big endian format
643 std::shared_ptr<Buffer>
644 AMF::encodeNullString()
645 {
646 //    GNASH_REPORT_FUNCTION;
647     std::uint16_t length;
648 
649     std::shared_ptr<Buffer> buf(new Buffer(AMF_HEADER_SIZE));
650     *buf = Element::STRING_AMF0;
651     // when a string is stored in an element, we add a NULL terminator so
652     // it can be printed by to_string() efficiently. The NULL terminator
653     // doesn't get written when encoding a string as it has a byte count
654     // instead.
655     length = 0;
656     *buf += length;
657 
658     return buf;
659 }
660 
661 /// \brief Write an AMF element
662 ///
663 /// This encodes the data supplied to an AMF formatted one. As the
664 /// memory is allocatd within this function, you *must* free the
665 /// memory used for each element or you'll leak memory.
666 ///
667 /// A "packet" (or element) in AMF is a byte code, followed by the
668 /// data. Sometimes the data is prefixed by a count, and sometimes
669 /// it's terminated with a 0x09. Ya gotta love these flaky ad-hoc
670 /// formats.
671 ///
672 /// All Numbers are 64 bit, big-endian (network byte order) entities.
673 ///
674 /// All strings are in multibyte format, which is to say, probably
675 /// normal ASCII. It may be that these need to be converted to wide
676 /// characters, but for now we just leave them as standard multibyte
677 /// characters.
678 
679 /// \brief Encode an Element to it's serialized representation.
680 ///
681 /// @param el A smart pointer to the Element to encode.
682 ///
683 /// @return a binary AMF packet in big endian format
684 std::shared_ptr<Buffer>
685 AMF::encodeElement(std::shared_ptr<cygnal::Element> el)
686 {
687     return encodeElement(*el);
688 }
689 
690 std::shared_ptr<Buffer>
691 AMF::encodeElement(const cygnal::Element& el)
692 {
693 //    GNASH_REPORT_FUNCTION;
694     std::shared_ptr<Buffer> buf;
695     // Encode the element's data
696     switch (el.getType()) {
697       case Element::NOTYPE:
698 	  return buf;
699 	  break;
700       case Element::NUMBER_AMF0:
701       {
702   //	  std::shared_ptr<Buffer> encnum = AMF::encodeNumber(el.to_number());
703   //	  *buf += encnum;
704 	  buf = AMF::encodeNumber(el.to_number());
705           break;
706       }
707       case Element::BOOLEAN_AMF0:
708       {
709 // 	  std::shared_ptr<Buffer> encbool = AMF::encodeBoolean(el.to_bool());
710 // 	  *buf += encodeBoolean(el.to_bool());
711 // 	  *buf += encbool;
712 	  buf = AMF::encodeBoolean(el.to_bool());
713           break;
714       }
715       case Element::STRING_AMF0:
716       {
717   //	  std::shared_ptr<Buffer> encstr = AMF::encodeString(el.to_string());
718 	  //	  *buf += encstr;
719 	  if (el.getDataSize() == 0) {
720 	      buf = encodeNullString();
721 	  } else {
722 	      buf = encodeString(el.to_string());
723 	  }
724 	  break;
725       }
726       case Element::OBJECT_AMF0:
727 	  buf = encodeObject(el);
728           break;
729       case Element::MOVIECLIP_AMF0:
730 	  buf = encodeMovieClip(el.to_reference(), el.getDataSize());
731           break;
732       case Element::NULL_AMF0:
733 	  //	  *buf += Element::NULL_AMF0;
734 	  buf = encodeNull();
735           break;
736       case Element::UNDEFINED_AMF0:
737 	  //	  *buf += Element::UNDEFINED_AMF0;
738 	  buf = encodeUndefined();
739 	  break;
740       case Element::REFERENCE_AMF0:
741 	  buf = encodeReference(el.to_short());
742           break;
743       case Element::ECMA_ARRAY_AMF0:
744 	  buf = encodeECMAArray(el);
745           break;
746 	  // The Object End gets added when creating the object, so we can just ignore it here.
747       case Element::OBJECT_END_AMF0:
748 	  buf = encodeObjectEnd();
749           break;
750       case Element::STRICT_ARRAY_AMF0:
751       {
752 	  buf = AMF::encodeStrictArray(el);
753           break;
754       }
755       case Element::DATE_AMF0:
756       {
757   //	  std::shared_ptr<Buffer> encdate = AMF::encodeNumber(el.to_number());
758 	  buf = AMF::encodeDate(el.to_reference());
759           break;
760       }
761       case Element::LONG_STRING_AMF0:
762 	  buf = encodeLongString(el.to_reference(), el.getDataSize());
763           break;
764       case Element::UNSUPPORTED_AMF0:
765 	  buf = encodeUnsupported();
766           break;
767       case Element::RECORD_SET_AMF0:
768 	  buf = encodeRecordSet(el.to_reference(), el.getDataSize());
769           break;
770       case Element::XML_OBJECT_AMF0:
771 	  buf = encodeXMLObject(el.to_reference(), el.getDataSize());
772           // Encode an XML object. The data follows a 4 byte length
773           // field. (which must be big-endian)
774           break;
775       case Element::TYPED_OBJECT_AMF0:
776 	  buf = encodeTypedObject(el);
777           break;
778 // 	  // This is a Gnash specific value
779 //       case Element::VARIABLE:
780 //       case Element::FUNCTION:
781 //          break;
782       case Element::AMF3_DATA:
783 	  gnash::log_error(_("FIXME: got AMF3 data type"));
784 	  break;
785       default:
786 	  buf.reset();
787           break;
788     };
789 
790     // If the name field is set, it's a property, followed by the data
791     std::shared_ptr<Buffer> bigbuf;
792     if (el.getName() && (el.getType() != Element::TYPED_OBJECT_AMF0)) {
793 	if (buf) {
794 	    bigbuf.reset(new cygnal::Buffer(el.getNameSize() + sizeof(std::uint16_t) + buf->size()));
795 	} else {
796 	    bigbuf.reset(new cygnal::Buffer(el.getNameSize() + sizeof(std::uint16_t)));
797 	}
798 
799 	// Add the length of the string for the name of the variable
800 	size_t length = el.getNameSize();
801 	std::uint16_t enclength = length;
802 	swapBytes(&enclength, 2);
803 	*bigbuf = enclength;
804 	// Now the name itself
805 	std::string name = el.getName();
806 	if (name.size() > 0) {
807 	    *bigbuf += name;
808 	}
809 	if (buf) {
810 	    *bigbuf += buf;
811 	}
812 	return bigbuf;
813     }
814 
815     return buf;
816 }
817 
818 /// Encode a variable to it's serialized representation.
819 ///
820 /// @param el A smart pointer to the Element to encode.
821 ///
822 /// @return a binary AMF packet in big endian format
823 std::shared_ptr<Buffer>
824 AMF::encodeProperty(std::shared_ptr<cygnal::Element> el)
825 {
826 //    GNASH_REPORT_FUNCTION;
827     size_t outsize;
828 
829     outsize = el->getNameSize() + el->getDataSize() + AMF_PROP_HEADER_SIZE;
830 
831     std::shared_ptr<Buffer> buf(new Buffer(outsize));
832     _totalsize += outsize;
833 
834     // Add the length of the string for the name of the property
835     size_t length = el->getNameSize();
836     std::uint16_t enclength = length;
837     swapBytes(&enclength, 2);
838     *buf = enclength;
839 
840     if (el->getName()) {
841 	std::string name = el->getName();
842 	if (name.size() > 0) {
843 	    *buf += name;
844 	}
845     }
846 
847     // Add the type of the property's data
848     *buf += el->getType();
849     // Booleans appear to be encoded weird. Just a short after
850     // the type byte that's the value.
851     switch (el->getType()) {
852       case Element::BOOLEAN_AMF0:
853 //  	  enclength = el->to_bool();
854 //  	  buf->append(enclength);
855   	  *buf += el->to_bool();
856 	  break;
857       case Element::NUMBER_AMF0:
858 	  if (el->to_reference()) {
859 	      swapBytes(el->to_reference(), AMF0_NUMBER_SIZE);
860 	      buf->append(el->to_reference(), AMF0_NUMBER_SIZE);
861 	  }
862 	  break;
863       default:
864 	  enclength = el->getDataSize();
865 	  swapBytes(&enclength, 2);
866 	  *buf += enclength;
867 	  // Now the data for the property
868 	  buf->append(el->to_reference(), el->getDataSize());
869     }
870 
871     return buf;
872 }
873 
874 /// \brief Extract an AMF object from an array of raw bytes.
875 ///
876 /// @param buf A smart pointer to a Buffer to parse the data from.
877 ///
878 /// @return A smart ptr to an Element.
879 ///
880 /// @remarks May throw a ParserException
881 std::shared_ptr<cygnal::Element>
882 AMF::extractAMF(std::shared_ptr<Buffer> buf)
883 {
884 //    GNASH_REPORT_FUNCTION;
885     std::uint8_t* start = buf->reference();
886     std::uint8_t* tooFar = start+buf->size();
887 
888     return extractAMF(start, tooFar);
889 }
890 
891 /// \brief Extract an AMF object from an array of raw bytes.
892 ///	An AMF object is one of the support data types.
893 ///
894 /// @param in A real pointer to the raw data to start parsing from.
895 ///
896 /// @param tooFar A pointer to one-byte-past the last valid memory
897 ///	address within the buffer.
898 ///
899 /// @return A smart ptr to an Element.
900 ///
901 /// @remarks May throw a ParserException
902 std::shared_ptr<cygnal::Element>
903 AMF::extractAMF(std::uint8_t *in, std::uint8_t* tooFar)
904 {
905 //    GNASH_REPORT_FUNCTION;
906 
907     std::uint8_t *tmpptr = in;
908     std::uint16_t length;
909     std::shared_ptr<cygnal::Element> el(new Element);
910 
911     if (in == nullptr) {
912         gnash::log_error(_("AMF body input data is NULL"));
913         return el;
914     }
915 
916     std::map<std::uint16_t, cygnal::Element> references;
917 
918     // All elements look like this:
919     // the first two bytes is the length of name of the element
920     // Then the next bytes are the element name
921     // After the element name there is a type byte. If it's a Number type, then
922     // 8 bytes are read If it's a String type, then there is a count of
923     // characters, then the string value
924 
925     // Get the type of the element, which is a single byte.
926     // This type casting looks like a stupid mistake, but it's
927     // mostly to make valgrind shut up, as it has a tendency to
928     // complain about legit code when it comes to all this byte
929     // manipulation stuff.
930     AMF amf_obj;
931 
932     // Jump through hoops to get the type so valgrind stays happy
933     //    char c = *(reinterpret_cast<char *>(tmpptr));
934 
935     if (tooFar - tmpptr < 1) {
936         gnash::log_error(_("AMF data too short to contain type field"));
937         return el;
938     }
939 
940     Element::amf0_type_e type = static_cast<Element::amf0_type_e>(*tmpptr);
941     // skip past the header type field byte
942     ++tmpptr;
943 
944     switch (type) {
945         case Element::NOTYPE:
946         {
947 	    gnash::log_error(_("Element has no type!"));
948 	    break;
949 	}
950         case Element::NUMBER_AMF0:
951         {
952             // Make sure this isn't less than 0. We check this above at
953             // the moment.
954             assert(tooFar >= tmpptr);
955 
956             if (static_cast<size_t>(tooFar - tmpptr) < sizeof(const double)) {
957                 gnash::log_error(_("AMF data segment too short to contain"
958                             "type NUMBER"));
959 		el.reset();
960                 return el;
961             }
962             double swapped = *(reinterpret_cast<const double*>(tmpptr));
963             swapBytes(&swapped, cygnal::AMF0_NUMBER_SIZE);
964             el->makeNumber(swapped);
965             tmpptr += AMF0_NUMBER_SIZE; // all numbers are 8 bit big endian
966             break;
967         }
968         case Element::BOOLEAN_AMF0:
969             el->makeBoolean(tmpptr);
970             tmpptr += 1;		// sizeof(bool) isn't always 1 for all compilers
971             break;
972         case Element::STRING_AMF0:
973             // get the length of the name
974             length = ntohs((*(std::uint16_t *)tmpptr) & 0xffff);
975             tmpptr += sizeof(std::uint16_t);
976             if (length >= SANE_STR_SIZE) {
977                 gnash::log_error(_("%d bytes for a string is over the safe "
978                                    "limit of %d, line %d"), length,
979                         SANE_STR_SIZE, __LINE__);
980                 el.reset();
981                 return el;
982             }
983             //log_debug(_("AMF String length is: %d"), length);
984             if (length > 0) {
985                 // get the name of the element
986                 el->makeString(tmpptr, length);
987                 //log_debug(_("AMF String is: %s"), el->to_string());
988                 tmpptr += length;
989             } else {
990                 el->setType(Element::STRING_AMF0);
991             }
992             break;
993         case Element::OBJECT_AMF0:
994         {
995             el->makeObject();
996             while (tmpptr < tooFar) {
997                 // FIXME: was tooFar - AMF_HEADER_SIZE)
998                 if (*tmpptr+3 == TERMINATOR) {
999                     //log_debug("No data associated with Property "
1000                     //"in object");
1001                     tmpptr++;
1002                     break;
1003                 }
1004                 std::shared_ptr<cygnal::Element> child =
1005                     amf_obj.extractProperty(tmpptr, tooFar);
1006                 if (child == nullptr) {
1007                     // skip past zero length string (2 bytes), null
1008                     // (1 byte) and end object (1 byte)
1009                     //tmpptr += 3;
1010                     break;
1011                 }
1012                 //child->dump();
1013                 el->addProperty(child);
1014                 tmpptr += amf_obj.totalsize();
1015             }
1016             tmpptr += AMF_HEADER_SIZE;		// skip past the terminator bytes
1017             break;
1018         }
1019         case Element::MOVIECLIP_AMF0:
1020             gnash::log_debug(_("AMF0 MovieClip frame"));
1021             break;
1022         case Element::NULL_AMF0:
1023             el->makeNull();
1024             break;
1025         case Element::UNDEFINED_AMF0:
1026             el->makeUndefined();
1027             break;
1028         case Element::REFERENCE_AMF0:
1029         {
1030             length = ntohs((*(std::uint16_t *)tmpptr) & 0xffff);
1031             tmpptr += sizeof(std::uint16_t);
1032             el->makeReference(length);
1033             // FIXME: connect reference Element to the object
1034             // pointed to by the index.
1035             tmpptr += 3;
1036             break;
1037         }
1038         // An ECMA array is composed of any of the data types. Much
1039         // like an Object, the ECMA array is terminated by the
1040         // end of object bytes, so parse till then.
1041         case Element::ECMA_ARRAY_AMF0:
1042         {
1043             el->makeECMAArray();
1044             tmpptr += sizeof(std::uint32_t);
1045 #if 1
1046             while (tmpptr < tooFar) { // FIXME: was tooFar - AMF_HEADER_SIZE)
1047                 if (*tmpptr+3 == TERMINATOR) {
1048           //		  log_debug("No data associated with Property in object");
1049                     tmpptr++;
1050                     break;
1051                 }
1052                 std::shared_ptr<cygnal::Element> child =
1053                     amf_obj.extractProperty(tmpptr, tooFar);
1054                 if (child == nullptr) {
1055                     // skip past zero length string (2 bytes),
1056                     // null (1 byte) and end object (1 byte)
1057                     //tmpptr += 3;
1058                     break;
1059                 }
1060       //	      child->dump();
1061                 el->addProperty(child);
1062                 tmpptr += amf_obj.totalsize();
1063             }
1064             tmpptr += AMF_HEADER_SIZE;		// skip past the terminator bytes
1065             break;
1066 #else
1067             // get the number of elements in the array
1068             std::uint32_t items =
1069                 ntohl((*(std::uint32_t *)tmpptr) & 0xffffffff);
1070 
1071             tmpptr += sizeof(std::uint32_t);
1072             while (items--) {
1073                 std::shared_ptr<cygnal::Element> child =
1074                     amf_obj.extractProperty(tmpptr, tooFar);
1075                 if (child == 0) {
1076                     break;
1077                 }
1078                 child->dump();
1079                 el->addProperty(child);
1080                 tmpptr += amf_obj.totalsize();
1081             }
1082             tmpptr += AMF_HEADER_SIZE;		// skip past the terminator bytes
1083 #endif
1084             break;
1085         }
1086         case Element::OBJECT_END_AMF0:
1087             // A strict array is only numbers
1088             break;
1089         case Element::STRICT_ARRAY_AMF0:
1090         {
1091             el->makeStrictArray();
1092             // get the number of numbers in the array
1093             std::uint32_t items = ntohl((*(std::uint32_t *)tmpptr));
1094             // Skip past the length field to get to the start of the data
1095             tmpptr += sizeof(std::uint32_t);
1096             while (items) {
1097                 std::shared_ptr<cygnal::Element> child =
1098                     amf_obj.extractAMF(tmpptr, tooFar);
1099                 if (child == nullptr) {
1100                     break;
1101                 } else {
1102                 //child->dump();
1103                     el->addProperty(child);
1104                     tmpptr += amf_obj.totalsize();
1105                     --items;
1106                 }
1107             }
1108             break;
1109         }
1110         case Element::DATE_AMF0:
1111         {
1112             double swapped = *reinterpret_cast<const double*>(tmpptr);
1113             swapBytes(&swapped, cygnal::AMF0_NUMBER_SIZE);
1114             el->makeDate(swapped);
1115             tmpptr += AMF0_NUMBER_SIZE; // all dates are 8 bit big endian numbers
1116             break;
1117         }
1118         case Element::LONG_STRING_AMF0:
1119             el->makeLongString(tmpptr);
1120             break;
1121         case Element::UNSUPPORTED_AMF0:
1122             el->makeUnsupported(tmpptr);
1123             tmpptr += 1;
1124             break;
1125         case Element::RECORD_SET_AMF0:
1126             el->makeRecordSet(tmpptr);
1127             break;
1128         case Element::XML_OBJECT_AMF0:
1129             el->makeXMLObject(tmpptr);
1130             break;
1131         case Element::TYPED_OBJECT_AMF0:
1132         {
1133             el->makeTypedObject();
1134 
1135             length = ntohs((*(std::uint16_t *)tmpptr) & 0xffff);
1136             tmpptr += sizeof(std::uint16_t);
1137             if (length > 0) {
1138                 std::string name(reinterpret_cast<const char*>(tmpptr), length);
1139                 //log_debug("Typed object name is: %s", el->getName());
1140                 el->setName(name.c_str(), name.size());
1141             }
1142             // Don't read past the end
1143             if (tmpptr + length < tooFar) {
1144                 tmpptr += length;
1145             }
1146 
1147             while (tmpptr < tooFar - length) {
1148                 // FIXME: was tooFar - AMF_HEADER_SIZE)
1149                 if (*(tmpptr +3) == TERMINATOR) {
1150                     gnash::log_debug(_("Found object terminator byte"));
1151                     tmpptr++;
1152                     break;
1153                 }
1154                 std::shared_ptr<cygnal::Element> child =
1155                     amf_obj.extractProperty(tmpptr, tooFar);
1156                 if (child == nullptr) {
1157                     break;
1158                 }
1159                 el->addProperty(child);
1160                 tmpptr += amf_obj.totalsize();
1161             }
1162             //	  el->dump();
1163             tmpptr += AMF_HEADER_SIZE;		// skip past the terminator bytes
1164             break;
1165         }
1166         case Element::AMF3_DATA:
1167         default:
1168             gnash::log_unimpl(_("%s: type %d"), __PRETTY_FUNCTION__, (int)type);
1169             el.reset();
1170             return el;
1171     }
1172 
1173     // Calculate the offset for the next read
1174     _totalsize = (tmpptr - in);
1175 
1176     return el;
1177 }
1178 
1179 /// \brief Extract a Property.
1180 ///	A Property is a standard AMF object preceeded by a
1181 ///	length and an ASCII name field. These are only used
1182 ///	with higher level ActionScript objects.
1183 ///
1184 /// @param buf A smart pointer to an Buffer to parse the data from.
1185 ///
1186 /// @return A smart ptr to an Element.
1187 ///
1188 /// @remarks May throw a ParserException
1189 std::shared_ptr<cygnal::Element>
1190 AMF::extractProperty(std::shared_ptr<Buffer> buf)
1191 {
1192 //    GNASH_REPORT_FUNCTION;
1193 
1194     std::uint8_t* start = buf->reference();
1195     std::uint8_t* tooFar = start+buf->size();
1196     return extractProperty(start, tooFar);
1197 }
1198 
1199 /// \brief Extract a Property.
1200 ///	A Property is a standard AMF object preceeded by a
1201 ///	length and an ASCII name field. These are onicly used
1202 ///	with higher level ActionScript objects.
1203 ///
1204 /// @param in A real pointer to the raw data to start parsing from.
1205 ///
1206 /// @param tooFar A pointer to one-byte-past the last valid memory
1207 ///	address within the buffer.
1208 ///
1209 /// @return A smart ptr to an Element.
1210 ///
1211 /// @remarks May throw a ParserException
1212 std::shared_ptr<cygnal::Element>
1213 AMF::extractProperty(std::uint8_t *in, std::uint8_t* tooFar)
1214 {
1215 //    GNASH_REPORT_FUNCTION;
1216 
1217     std::uint8_t *tmpptr = in;
1218     std::uint16_t length;
1219     std::shared_ptr<cygnal::Element> el;
1220 
1221     length = ntohs((*(std::uint16_t *)tmpptr) & 0xffff);
1222     // go past the length bytes, which leaves us pointing at the raw data
1223     tmpptr += sizeof(std::uint16_t);
1224 
1225     // sanity check the length of the data. The length is usually only zero if
1226     // we've gone all the way to the end of the object.
1227 
1228     // valgrind complains if length is unintialized, which as we just set
1229     // length to a value, this is tottaly bogus, and I'm tired of
1230     // braindamaging code to keep valgrind happy.
1231     if (length <= 0) {
1232 // 	log_debug("No Property name, object done %x, %x", (void *)in, (void *)tooFar);
1233 	return el;
1234     }
1235 
1236 #if 0
1237     if (length + tmpptr > tooFar) {
1238 	log_error("%d bytes for a string is over the safe limit of %d. Putting the rest of the buffer into the string, line %d", length, SANE_STR_SIZE, __LINE__);
1239 	length = tooFar - tmpptr;
1240     }
1241 #else
1242     if (length >= SANE_STR_SIZE) {
1243 	gnash::log_error("%d bytes for a string is over the safe limit of %d. Putting the rest of the buffer into the string, line %d", length, SANE_STR_SIZE, __LINE__);
1244     }
1245 #endif
1246 
1247     // name is just debugging help to print cleaner, and should be removed later
1248 //    log_debug(_("AMF property name length is: %d"), length);
1249     std::string name(reinterpret_cast<const char *>(tmpptr), length);
1250 //    log_debug(_("AMF property name is: %s"), name);
1251     // Don't read past the end
1252     if (tmpptr + length < tooFar) {
1253 	tmpptr += length;
1254     }
1255 
1256     char c = *(reinterpret_cast<char *>(tmpptr));
1257     Element::amf0_type_e type = static_cast<Element::amf0_type_e>(c);
1258     // If we get a NULL object, there is no data. In that case, we only return
1259     // the name of the property.
1260     if (type == Element::NULL_AMF0) {
1261 	gnash::log_debug(_("No data associated with Property \"%s\""), name);
1262 	el.reset(new Element);
1263 	el->setName(name.c_str(), name.size());
1264 	tmpptr += 1;
1265 	// Calculate the offset for the next read
1266     } else {
1267 	// process the data with associated with the property.
1268 	// Go past the data to the start of the next AMF object, which
1269 	// should be a type byte.
1270 //	tmpptr += length;
1271 	el = extractAMF(tmpptr, tooFar);
1272 	if (el) {
1273 	    el->setName(name.c_str(), name.size()); // FIXME: arg, overwrites the name for TypedObjects
1274 	}
1275 	tmpptr += totalsize();
1276     }
1277 
1278     //delete name;
1279 
1280     // Calculate the offset for the next read
1281     _totalsize = (tmpptr - in);
1282 
1283     return el;
1284 }
1285 
1286 } // end of amf namespace
1287 
1288 // local Variables:
1289 // mode: C++
1290 // indent-tabs-mode: nil
1291 // End:
1292