/// Makes the LocalConnection object listen. /// /// The name is a symbolic name like "lc1", that is used by the /// send() command to signify which local connection to send the /// object to. // /// When connect is called, this object adds its domain + name plus some /// other bits of information to the listeners portion of the shared memory. /// It also sets the initial bytes of the shared memory to a set /// pattern. // /// The connection will fail if a listener with the same id (domain + name) /// already exists. ActionScript isn't informed of this failure. void LocalConnection_as::connect(const std::string& name) { assert(!name.empty()); _name = name; if (!_shm.attach()) { log_error("Failed to open shared memory segment"); return; } SharedMem::iterator ptr = _shm.begin(); // We can't connect if there is already a listener with the same name. if (!addListener(_domain + ":" + _name, _shm)) { return; } const char i[] = { 1, 0, 0, 0, 1, 0, 0, 0 }; std::copy(i, i + 8, ptr); movie_root& mr = getRoot(owner()); mr.addAdvanceCallback(this); _connected = true; return; }
void LocalConnection_as::update() { // Check whether local connection is disabled(!): brilliant choice of // function name. if (rcfile.getLocalConnection()) { log_security("Attempting to write to disabled LocalConnection!"); movie_root& mr = getRoot(owner()); mr.removeAdvanceCallback(this); return; } // No-op if already attached. Nothing to do if it fails, but we // should probably stop trying. if (!_shm.attach()) { log_error("Failed to attach shared memory segment"); return; } // We need the lock to prevent simultaneous reads/writes from other // processes. SharedMem::Lock lock(_shm); if (!lock.locked()) { log_debug("Failed to get shm lock"); return; } SharedMem::iterator ptr = _shm.begin(); // First check timestamp data. // These are not network byte order by default, but not sure about // host byte order. const boost::uint32_t timestamp = readLong(ptr + 8); const boost::uint32_t size = readLong(ptr + 12); // As long as there is a timestamp in the shared memory, we mustn't // write anything. // // We check if this is data we are listening for. If it is, read it and // mark for overwriting. // // If not, we keep checking until the data has been overwritten by // another listener or until it's expired. If it's expired, we // mark for overwriting. if (timestamp) { // Start after 16-byte header. const boost::uint8_t* b = ptr + 16; // End at reported size of AMF sequence. const boost::uint8_t* end = b + size; amf::Reader rd(b, end, getGlobal(owner())); as_value a; // Get the connection name. That's all we need to remove expired // data. if (!rd(a)) { log_error("Invalid connection name data"); return; } const std::string& connection = a.to_string(); // Now check if data we wrote has expired. There's no really // reliable way of checking that we genuinely wrote it. if (_lastTime == timestamp) { const size_t timeout = 4 * 1000; VM& vm = getVM(owner()); const boost::uint32_t timeNow = getTimestamp(vm); if (timeNow - timestamp > timeout) { log_debug("Data %s expired at %s. Removing its target " "as a listener", timestamp, timeNow); removeListener(connection, _shm); markRead(_shm); _lastTime = 0; } } // If we are listening and the data is for us, get the rest of it // and call the method. if (_connected && connection == _domain + ":" + _name) { executeAMFFunction(owner(), rd); // Zero the timestamp bytes to signal that the shared memory // can be written again. markRead(_shm); } else { // The data has not expired and we didn't read it. Leave it // alone until it's expired or someone else has read it. return; } } // If we have no data to send, there's nothing more to do. if (_queue.empty()) { // ...except remove the callback if we aren't listening for anything. if (!_connected) { movie_root& mr = getRoot(owner()); mr.removeAdvanceCallback(this); } return; } // Get the first buffer. boost::shared_ptr<ConnectionData> cd = _queue.front(); _queue.pop_front(); // If the correct listener isn't there, iterate until we find one or // there aren't any left. while (!findListener(_domain + ":" + cd->name, _shm)) { if (_queue.empty()) { // Make sure we send the empty header later. cd->ts = 0; break; } cd = _queue.front(); _queue.pop_front(); } // Yes, there is data to send. const char i[] = { 1, 0, 0, 0, 1, 0, 0, 0 }; std::copy(i, i + arraySize(i), ptr); SimpleBuffer& buf = cd->data; SharedMem::iterator tmp = ptr + 8; writeLong(tmp, cd->ts); writeLong(tmp, cd->ts ? buf.size() : 0); std::copy(buf.data(), buf.data() + buf.size(), tmp); // Note the timestamp of our last send. We will keep calling update() // until the data has expired or been read. _lastTime = cd->ts; }