root/cygnal/libamf/amf_msg.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. encodeContextHeader
  2. encodeContextHeader
  3. encodeMsgHeader
  4. parseContextHeader
  5. parseContextHeader
  6. parseMessageHeader
  7. parseMessageHeader
  8. parseAMFPacket
  9. parseAMFPacket
  10. encodeAMFPacket
  11. encodeAMFPacket
  12. encodeMsgHeader
  13. dump
  14. dump
  15. dump

// amf.cpp:  AMF (Action Message Format) rpc marshalling, for Gnash.
// 
//   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
//   2011 Free Software Foundation, Inc
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

#include "log.h"
#include "GnashException.h"
#include "buffer.h"
#include "amf.h"
#include "amf_msg.h"
#include "element.h"
#include "network.h"
#include "GnashSystemNetHeaders.h"

#include <boost/shared_ptr.hpp>
#include <string>
#include <boost/cstdint.hpp> // For C99 int types

using gnash::GnashException;
using gnash::log_error;
using std::cout;
using std::endl;


namespace cygnal
{

boost::shared_ptr<cygnal::Buffer> 
AMF_msg::encodeContextHeader(boost::uint16_t version, boost::uint16_t headers,
                             boost::uint16_t messages)
{
//    GNASH_REPORT_FUNCTION;
    size_t size = sizeof(AMF_msg::context_header_t);
    boost::shared_ptr<cygnal::Buffer> buf (new cygnal::Buffer(size));

    // use a short as a temporary, as it turns out htons() returns a 32bit int
    // instead when compiling with -O2. This forces appending bytes to get the
    // right size.
    boost::uint16_t swapped = htons(version);
    *buf = swapped;
    swapped = htons(headers);
    *buf += swapped;
    swapped = htons(messages);
    *buf += swapped;
        
    return buf;
}

boost::shared_ptr<cygnal::Buffer>
AMF_msg::encodeContextHeader(AMF_msg::context_header_t *head)
{
//    GNASH_REPORT_FUNCTION;
    return encodeContextHeader(head->version, head->headers, head->messages);
}

//  example message header::
//  00 06 67 65 74 77 61 79                <- getway, message #1
//  00 04 2f 32 32 39                      <- /229, operation name
//  00 00 00 0e                            <- byte length of message
boost::shared_ptr<cygnal::Buffer>
AMF_msg::encodeMsgHeader(AMF_msg::message_header_t *head)
{
//    GNASH_REPORT_FUNCTION;
    // The size of the buffer are the two strings, their lenght fields, and the integer.
//     size_t size = head->target.size() + head->response.size() + sizeof(boost::uint32_t)
//         + (sizeof(boost::uint16_t) * 2);
    boost::shared_ptr<cygnal::Buffer> buf (new cygnal::Buffer(sizeof(AMF_msg::message_header_t)));

    // Encode the target URI, which usually looks something like ."getway"
    boost::uint16_t length = head->target.size();    
    *buf = length;
    *buf += head->target;

    // Encode the response URI, which usually looks something like "/229"
    length = head->response.size();
    *buf += length;
    *buf += head->target;

    // Encode the size of the encoded message
    *buf += static_cast<boost::uint32_t>(head->size);
    
    return buf;
}

// These methods parse the raw data of the AMF packet
boost::shared_ptr<AMF_msg::context_header_t>
AMF_msg::parseContextHeader(cygnal::Buffer &data)
{
//    GNASH_REPORT_FUNCTION;
    return parseContextHeader(data.reference(), data.size());
}

boost::shared_ptr<AMF_msg::context_header_t>
AMF_msg::parseContextHeader(boost::uint8_t *data, size_t /* size */)
{
//    GNASH_REPORT_FUNCTION;
    boost::shared_ptr<AMF_msg::context_header_t> msg (new AMF_msg::context_header_t);

    boost::uint16_t tmpnum = *reinterpret_cast<boost::uint16_t *>(data);
    msg->version  = tmpnum;
    tmpnum = *reinterpret_cast<boost::uint16_t *>(data + sizeof(boost::uint16_t));
    msg->headers   = ntohs(tmpnum);
    tmpnum = *reinterpret_cast<boost::uint16_t *>(data + sizeof(boost::uint32_t));
    msg->messages = ntohs(tmpnum);

    return msg;
}

boost::shared_ptr<AMF_msg::message_header_t>
AMF_msg::parseMessageHeader(cygnal::Buffer &data)
{
//    GNASH_REPORT_FUNCTION;
    return parseMessageHeader(data.reference(), data.size());
}

boost::shared_ptr<AMF_msg::message_header_t>
AMF_msg::parseMessageHeader(boost::uint8_t *data, size_t size)
{
//    GNASH_REPORT_FUNCTION;
    AMF amf;
    boost::uint8_t *tmpptr = data;
    boost::shared_ptr<AMF_msg::message_header_t> msg (new AMF_msg::message_header_t);

    // The target is a standard length->bytes field
    boost::uint16_t length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
    if (length == 0) {
        boost::format msg("Length of string shouldn't be zero! amf_msg.cpp::%1%(): %2%");
        msg % __FUNCTION__ % __LINE__;
        throw GnashException(msg.str());
    }
    tmpptr += sizeof(boost::uint16_t);
    std::string str1(reinterpret_cast<const char *>(tmpptr), length);
    msg->target = str1;
    if ((tmpptr - data) > static_cast<int>(size)) {
        boost::format msg("Trying to read past the end of data! Wants %1% bytes, given %2% bytes");
        msg % length % size;
        throw GnashException(msg.str());
    } else {
        tmpptr += length;
    }
    
    // The response is a standard length->bytes field
    length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
    if (length == 0) {
        boost::format msg("Length of string shouldn't be zero! amf_msg.cpp::%1%(): %2%");
        msg % __FUNCTION__ % __LINE__;
        throw GnashException(msg.str());
    }
    tmpptr += sizeof(boost::uint16_t);
    std::string str2(reinterpret_cast<const char *>(tmpptr), length);
    msg->response = str2;
    tmpptr += length;
    if ((tmpptr - data) > static_cast<int>(size)) {
        boost::format msg("Trying to read past the end of data! Wants %1% bytes, given %2% bytes");
        msg % length % size;
        throw GnashException(msg.str());
    }    

    // The length is a 4 word integer
    msg->size = ntohl((*(boost::uint32_t *)tmpptr));

    if (msg->target.empty()) {
        log_error("AMF Message \'target\' field missing!");
    }
    if (msg->response.empty()) {
        log_error("AMF Message \'reply\' field missing!");
    }
    if (msg->size == 0) {
        log_error("AMF Message \'size\' field missing!");
    } else {
        msg->size = size;
    }

//    AMF_msg::dump(*msg);
    return msg;
}

boost::shared_ptr<AMF_msg::context_header_t>
AMF_msg::parseAMFPacket(cygnal::Buffer &data)
{
//    GNASH_REPORT_FUNCTION;
    return parseAMFPacket(data.reference(), data.size());
}

boost::shared_ptr<AMF_msg::context_header_t>
AMF_msg::parseAMFPacket(boost::uint8_t *data, size_t size)
{
    GNASH_REPORT_FUNCTION;
//    _messages.push_back();
    boost::uint8_t *ptr = data + sizeof(AMF_msg::context_header_t);
    boost::shared_ptr<context_header_t> header = AMF_msg::parseContextHeader(data, size);

//     log_debug("%s: %s", __PRETTY_FUNCTION__, hexify(data, size, true));
    
    AMF amf;
    /// Read all the messages from the AMF packet
    try {
        for (int i=0; i<header->messages; i++) {
            boost::shared_ptr<AMF_msg::amf_message_t> msgpkt(new AMF_msg::amf_message_t);
            boost::shared_ptr<AMF_msg::message_header_t> msghead = AMF_msg::parseMessageHeader(ptr, size);
            if (msghead) {
                ptr += msghead->target.size() + msghead->response.size()
                    + (sizeof(boost::uint16_t) * 2)
                    + (sizeof(boost::uint32_t));
                boost::shared_ptr<cygnal::Element> el = amf.extractAMF(ptr, ptr+size);
                msgpkt->header.target = msghead->target;
                msgpkt->header.response = msghead->response;
                msgpkt->header.size = msghead->size;
                msgpkt->data = el;
                ptr += amf.totalsize();
                
                _messages.push_back(msgpkt);
            }
        }
    } catch(std::exception& e) {
        log_error("Error parsing the AMF packet: \n\t%s", e.what());
    }
        
    return header;
}

boost::shared_ptr<cygnal::Buffer>
AMF_msg::encodeAMFPacket(const std::string & /* target */,
                         const std::string & /*response */, size_t /* size */)
{
//    GNASH_REPORT_FUNCTION;

    return encodeAMFPacket();
}

boost::shared_ptr<cygnal::Buffer>
AMF_msg::encodeAMFPacket()
{
//    GNASH_REPORT_FUNCTION;
    boost::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer);

    // Encode the packet header
    boost::shared_ptr<cygnal::Buffer> buf1 = encodeContextHeader(0, 0, _messages.size());
    *buf = buf1;

    // Now encode all the messages

    std::vector<boost::shared_ptr<AMF_msg::amf_message_t> >::iterator it;
    for (it = _messages.begin(); it != _messages.end(); it++) {
        boost::shared_ptr<AMF_msg::amf_message_t> msg = (*(it));

        boost::shared_ptr<cygnal::Buffer> buf2 = encodeMsgHeader(msg->header.target,
                                                             msg->header.response,
                                                             msg->header.size);

//      AMF_msg::dump(msg->header);
//      msg->data->dump();
        boost::shared_ptr<cygnal::Buffer> buf3 = msg->data->encode();
        *buf += buf2;
        *buf += buf3;
    }

    return buf;
}

boost::shared_ptr<cygnal::Buffer>
AMF_msg::encodeMsgHeader(const std::string &target,
                         const std::string &response, size_t size)
{
//    GNASH_REPORT_FUNCTION;
    size_t total = target.size() + sizeof(boost::uint16_t);
    total += response.size() + sizeof(boost::uint16_t);
    total += sizeof(boost::uint32_t);
    
    boost::shared_ptr<cygnal::Buffer> buf (new cygnal::Buffer(total));
    boost::uint16_t length = target.size();
    swapBytes(&length, sizeof(boost::uint16_t));
    *buf += length;
    *buf += target;

    length = response.size();
    swapBytes(&length, sizeof(boost::uint16_t));
    *buf += length;
    *buf += response;

    boost::uint32_t swapped = htonl(size);
    *buf += swapped;
    
    return buf;
}    

void
AMF_msg::dump(AMF_msg::message_header_t &data)
{
//    GNASH_REPORT_FUNCTION;
    cout << "Target is: " << data.target << endl;
    cout << "Response is: " << data.response << endl;
    cout << "Data size is: " << data.size << endl;
}

void
AMF_msg::dump(AMF_msg::context_header_t &data)
{
//    GNASH_REPORT_FUNCTION;
    cout << "AMF Version: " << data.version << endl;
    cout << "Number of headers: " << data.headers << endl;
    cout << "Number of messages: " << data.messages << endl;
}

void
AMF_msg::dump()
{
//    GNASH_REPORT_FUNCTION;
    cout << "AMF Packet has " << _messages.size() << " messages." << endl;
    std::vector<boost::shared_ptr<AMF_msg::amf_message_t> >::iterator it;
    for (it = _messages.begin(); it != _messages.end(); it++) {
        boost::shared_ptr<AMF_msg::amf_message_t> msg = (*(it));
        AMF_msg::dump(msg->header);
        msg->data->dump();
    }
}


} // end of amf namespace

// local Variables:
// mode: C++
// indent-tabs-mode: t
// End:

/* [<][>][^][v][top][bottom][index][help] */