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