void RTMP::call(const SimpleBuffer& amf) { RTMPPacket p(amf.size()); setupInvokePacket(p); // Copy the data. p.buffer->append(amf.data(), amf.size()); sendPacket(p); }
/// Ensure that each buffer has appropriate padding for the decoder. // /// Note: all callers passing a SimpleBuffer should already do this, /// so this is a paranoid check. void ensurePadding(SimpleBuffer& data, media::MediaHandler* m) { const size_t padding = m ? m->getInputPaddingSize() : 0; if (data.capacity() - data.size() < padding) { log_error(_("Sound data creator didn't appropriately pad " "buffer. We'll do so now, but will cost memory copies.")); data.reserve(data.size() + padding); } }
void RTMP::play(const SimpleBuffer& buf, int streamID) { RTMPPacket packet(buf.size()); packet.header.channel = CHANNEL_VIDEO; packet.header.packetType = PACKET_TYPE_INVOKE; packet.header._streamID = streamID; packet.buffer->append(buf.data(), buf.size()); sendPacket(packet); }
void push_amf(const SimpleBuffer &amf) { //GNASH_REPORT_FUNCTION; _postdata.append(amf.data(), amf.size()); queued_count++; }
/// Return the size of the data plus header. size_t size() const { if (!_data) return 0; SimpleBuffer buf; // The header comprises 2 bytes and a length field of 4 bytes. if (encodeData(_name, *_data, buf)) { return buf.size() + 6; } return 0; }
bool HTTPRemotingHandler::advance() { #ifdef GNASH_DEBUG_REMOTING log_debug("advancing HTTPRemotingHandler"); #endif if (_connection) { #ifdef GNASH_DEBUG_REMOTING log_debug("have connection"); #endif // Fill last chunk before reading in the next size_t toRead = reply.capacity() - reply.size(); if (! toRead) toRead = NCCALLREPLYCHUNK; #ifdef GNASH_DEBUG_REMOTING log_debug("Attempt to read %d bytes", toRead); #endif // See if we need to allocate more bytes for the next // read chunk if (reply.capacity() < reply.size() + toRead) { // if _connection->size() >= 0, reserve for it, so // if HTTP Content-Length response header is correct // we'll be allocating only once for all. const size_t newCapacity = reply.size() + toRead; #ifdef GNASH_DEBUG_REMOTING log_debug("NetConnection.call: reply buffer capacity (%d) " "is too small to accept next %d bytes of chunk " "(current size is %d). Reserving %d bytes.", reply.capacity(), toRead, reply.size(), newCapacity); #endif reply.reserve(newCapacity); #ifdef GNASH_DEBUG_REMOTING log_debug(" after reserve, new capacity is %d", reply.capacity()); #endif } const int read = _connection->readNonBlocking(reply.data() + reply.size(), toRead); if (read > 0) { #ifdef GNASH_DEBUG_REMOTING log_debug("read '%1%' bytes: %2%", read, hexify(reply.data() + reply.size(), read, false)); #endif reply.resize(reply.size() + read); } // There is no way to tell if we have a whole amf reply without // parsing everything // // The reply format has a header field which specifies the // number of bytes in the reply, but potlatch sends 0xffffffff // and works fine in the proprietary player // // For now we just wait until we have the full reply. // // FIXME make this parse on other conditions, including: 1) when // the buffer is full, 2) when we have a "length in bytes" value // thas is satisfied if (_connection->bad()) { log_debug("connection is in error condition, calling " "NetConnection.onStatus"); reply.resize(0); reply_start = 0; // reset connection before calling the callback _connection.reset(); // This is just a guess, but is better than sending // 'undefined' _nc.notifyStatus(NetConnection_as::CALL_FAILED); } else if (_connection->eof()) { if (reply.size() > 8) { #ifdef GNASH_DEBUG_REMOTING log_debug("hit eof"); #endif boost::uint16_t li; const boost::uint8_t *b = reply.data() + reply_start; const boost::uint8_t *end = reply.data() + reply.size(); amf::Reader rd(b, end, getGlobal(_nc.owner())); // parse header b += 2; // skip version indicator and client id // NOTE: this looks much like parsing of an OBJECT_AMF0 boost::int16_t si = readNetworkShort(b); b += 2; // number of headers uint8_t headers_ok = 1; if (si != 0) { #ifdef GNASH_DEBUG_REMOTING log_debug("NetConnection::call(): amf headers " "section parsing"); #endif as_value tmp; for (size_t i = si; i > 0; --i) { if(b + 2 > end) { headers_ok = 0; break; } si = readNetworkShort(b); b += 2; // name length if(b + si > end) { headers_ok = 0; break; } std::string headerName((char*)b, si); // end-b); #ifdef GNASH_DEBUG_REMOTING log_debug("Header name %s", headerName); #endif b += si; if ( b + 5 > end ) { headers_ok = 0; break; } b += 5; // skip past bool and length long if(!rd(tmp)) { headers_ok = 0; break; } #ifdef GNASH_DEBUG_REMOTING log_debug("Header value %s", tmp); #endif { // method call for each header // FIXME: it seems to me that the call should happen VM& vm = getVM(_nc.owner()); string_table& st = vm.getStringTable(); string_table::key key = st.find(headerName); #ifdef GNASH_DEBUG_REMOTING log_debug("Calling NetConnection.%s(%s)", headerName, tmp); #endif callMethod(&_nc.owner(), key, tmp); } } } if(headers_ok == 1) { si = readNetworkShort(b); b += 2; // number of replies // TODO consider counting number of replies we // actually parse and doing something if it // doesn't match this value (does it matter? if(si > 0) { // parse replies until we get a parse error or we reach the end of the buffer while(b < end) { if(b + 2 > end) break; si = readNetworkShort(b); b += 2; // reply length if(si < 4) { // shorted valid response is '/1/a' log_error("NetConnection::call(): reply message name too short"); break; } if(b + si > end) break; // Reply message is: '/id/methodName' int ns = 1; // next slash position while (ns<si-1 && *(b+ns) != '/') ++ns; if ( ns >= si-1 ) { std::string msg( reinterpret_cast<const char*>(b), si); log_error("NetConnection::call(): invalid " "reply message name (%s)", msg); break; } std::string id(reinterpret_cast<const char*>(b), ns); std::string methodName( reinterpret_cast<const char*>(b+ns+1), si-ns-1); b += si; // parse past unused string in header if(b + 2 > end) break; si = readNetworkShort(b); b += 2; // reply length if(b + si > end) break; b += si; // this field is supposed to hold the // total number of bytes in the rest of // this particular reply value, but // openstreetmap.org (which works great // in the adobe player) sends // 0xffffffff. So we just ignore it if(b + 4 > end) break; li = readNetworkLong(b); b += 4; // reply length #ifdef GNASH_DEBUG_REMOTING log_debug("about to parse amf value"); #endif // this updates b to point to the next unparsed byte as_value replyval; if (!rd(replyval)) { log_error("parse amf failed"); // this will happen if we get // bogus data, or if the data runs // off the end of the buffer // provided, or if we get data we // don't know how to parse break; } #ifdef GNASH_DEBUG_REMOTING log_debug("parsed amf"); #endif // update variable to show how much we've parsed reply_start = b - reply.data(); // if actionscript specified a callback object, // call it as_object* callback = pop_callback(id); if (callback) { string_table::key methodKey; if ( methodName == "onResult" ) { methodKey = NSV::PROP_ON_RESULT; } else if (methodName == "onStatus") { methodKey = NSV::PROP_ON_STATUS; } else { // NOTE: the pp is known to actually // invoke the custom method, but with 7 // undefined arguments (?) log_error("Unsupported HTTP Remoting " "response callback: '%s' " "(size %d)", methodName, methodName.size()); continue; } #ifdef GNASH_DEBUG_REMOTING log_debug("calling onResult callback"); #endif // FIXME check if above line can fail and we // have to react callMethod(callback, methodKey, replyval); #ifdef GNASH_DEBUG_REMOTING log_debug("callback called"); #endif } else { log_error("Unknown HTTP Remoting response identifier '%s'", id); } } } } } else { log_error("Response from remoting service < 8 bytes"); } #ifdef GNASH_DEBUG_REMOTING log_debug("deleting connection"); #endif _connection.reset(); reply.resize(0); reply_start = 0; } } if(!_connection && queued_count > 0) { //#ifdef GNASH_DEBUG_REMOTING log_debug("creating connection"); //#endif // set the "number of bodies" header (reinterpret_cast<boost::uint16_t*>(_postdata.data() + 4))[0] = htons(queued_count); std::string postdata_str(reinterpret_cast<char*>(_postdata.data()), _postdata.size()); #ifdef GNASH_DEBUG_REMOTING log_debug("NetConnection.call(): encoded args from %1% calls: %2%", queued_count, hexify(postdata.data(), postdata.size(), false)); #endif queued_count = 0; // TODO: it might be useful for a Remoting Handler to have a // StreamProvider member const StreamProvider& sp = getRunResources(_nc.owner()).streamProvider(); _connection.reset(sp.getStream(_url, postdata_str, _headers).release()); _postdata.resize(6); #ifdef GNASH_DEBUG_REMOTING log_debug("connection created"); #endif } if (_connection == 0) { // nothing more to do return false; } return true; }
/// Returns false if the data cannot be written to file. // /// If there is no data, the file is removed and the function returns true. bool SharedObject_as::flush(int space) const { /// This is called on on destruction of the SharedObject, or (allegedly) /// on a call to SharedObject.data, so _data is not guaranteed to exist. // /// The function should never be called from SharedObject.flush() when /// _data is 0. if (!_data) return false; if (space > 0) { log_unimpl("SharedObject.flush() called with a minimum disk space " "argument (%d), which is currently ignored", space); } const std::string& filespec = getFilespec(); if (!mkdirRecursive(filespec)) { log_error("Couldn't create dir for flushing SharedObject %s", filespec); return false; } #ifdef USE_SOL_READONLY log_debug(_("SharedObject %s not flushed (compiled as read-only mode)"), filespec); return false; #endif if (rcfile.getSOLReadOnly()) { log_security("Attempting to write object %s when it's SOL " "Read Only is set! Refusing...", filespec); return false; } // Open file std::ofstream ofs(filespec.c_str(), std::ios::binary); if (!ofs) { log_error("SharedObject::flush(): Failed opening file '%s' in " "binary mode", filespec.c_str()); return false; } // Encode data part. SimpleBuffer buf; if (!encodeData(_name, *_data, buf)) { std::remove(filespec.c_str()); return true; } // Encode header part. SimpleBuffer header; encodeHeader(buf.size(), header); // Write header ofs.write(reinterpret_cast<const char*>(header.data()), header.size()); if (!ofs) { log_error("Error writing SOL header"); return false; } // Write AMF data ofs.write(reinterpret_cast<const char*>(buf.data()), buf.size()); if (!ofs) { log_error("Error writing %d bytes to output file %s", buf.size(), filespec.c_str()); return false; } ofs.close(); log_security("SharedObject '%s' written to filesystem.", filespec); return true; }