"Fossies" - the Fresh Open Source Software Archive

Member "gnash-0.8.10/libcore/AMFConverter.cpp" (19 Jan 2012, 16040 Bytes) of package /linux/www/old/gnash-0.8.10.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    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:
   54     ObjectSerializer(Writer& w, VM& vm)
   55         :
   56         _writer(w),
   57         _st(vm.getStringTable()),
   58         _error(false)
   59     {}
   60     
   61     bool success() const { return !_error; }
   62 
   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
  115 Writer::writePropertyName(const std::string& name)
  116 {
  117     writePlainString(_buf, name, STRING_AMF0);
  118     return true;
  119 }
  120 
  121 bool
  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             boost::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
  251 Writer::writeString(const std::string& str)
  252 {
  253     write(_buf, str);
  254     return true;
  255 }
  256 
  257 bool
  258 Writer::writeNumber(double d)
  259 {
  260     write(_buf, d);
  261     return true;
  262 }
  263 
  264 bool
  265 Writer::writeBoolean(bool b)
  266 {
  267     write(_buf, b);
  268     return true;
  269 }
  270 
  271 
  272 bool
  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
  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
  293 Writer::writeData(const boost::uint8_t* data, size_t length)
  294 {
  295     _buf.append(data, length);
  296 }
  297 
  298 bool
  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*>(0);
  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
  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
  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 boost::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
  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 boost::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 boost::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
  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
  553 Reader::readReference()
  554 {
  555 
  556     if (_end - _pos < 2) {
  557         throw AMFException("Read past _end of buffer for reference index");
  558     }
  559     const boost::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
  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 boost::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