Update with current status
[gnash.git] / libcore / asobj / LocalConnection_as.cpp
blob705d3a93a441dd4f685e0c28411dfaf3571b9fa1
1 // LocalConnection.cpp: Connect two SWF movies & send objects, 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.
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
20 #include "LocalConnection_as.h"
22 #include <cerrno>
23 #include <cstring>
24 #include <cstdint> // for boost::?int??_t
25 #include <functional>
27 #include "GnashSystemIOHeaders.h"
29 #include "VM.h"
30 #include "movie_root.h"
31 #include "URL.h"
32 #include "log.h"
33 #include "fn_call.h"
34 #include "Global_as.h"
35 #include "NativeFunction.h"
36 #include "SharedMem.h"
37 #include "namedStrings.h"
38 #include "StringPredicates.h"
39 #include "as_value.h"
40 #include "AMFConverter.h"
41 #include "ClockTime.h"
42 #include "GnashAlgorithm.h"
43 #include "RunResources.h"
44 #include "StreamProvider.h"
47 /// From observing the behaviour of the pp, the following seem to be true.
49 /// (Behaviour may be different on other platforms).
51 /// Sending
52 ///
53 /// A sender checks the timestamp value. If it is zero, the data may be
54 /// overwritten. If it is not, we check whether we can delete it.
55 ///
56 /// Data expires after 4 seconds. Processes only delete data they (think they)
57 /// wrote. If the timestamp matches the timestamp of the last data we sent,
58 /// we assume it's our data. If it's expired, mark it for overwriting and
59 /// continue.
60 ///
61 /// We continue to check whether the data has expired as long as the data
62 /// is not marked for overwriting. No changes are made to the buffer by a
63 /// sender as long as the timestamp is there.
64 ///
65 /// Once the buffer is ready for writing, check if the correct listener is
66 /// present. If it is, send the first message in our queue and store its
67 /// timestamp. If the listener is not present, go through the queue until
68 /// a message for an existing listener is found. If none is found, the
69 /// last message in the buffer is sent with no timestamp. (It's not clear if
70 /// there's any point in sending it, but it happens).
72 /// Receiving
73 ///
74 /// A listener registers itself by adding its name to the listeners section
75 /// of the shared memory. The name is null-terminated and followed by a
76 /// further marker, which is of the form "::x\0::y\0". The x and y characters
77 /// are always numbers, e.g. ::3::4, ::3::1, ::3::2. We do not know the
78 /// significance of these numbers.
80 /// A listener merely checks whether the data has a timestamp and if the
81 /// data is intended for it (by reading the first string field). If it is,
82 /// the data is deserialized, the encoded function called, and the data
83 /// marked for deletion.
85 /// Functions are encoded in a particular order after the timestamp and length
86 /// fields:
87 /// 1. connection name (domain:connection) [string]
88 /// 2. domain [string]
89 /// {
90 /// 3. The following optional data:
91 /// [boolean] (always false?)
92 /// [boolean] (always false?)
93 /// [number] (e.g. 0, 1)
94 /// [number] (e.g. 8, 6)
95 /// 4. Sometimes the filename [string]. The presence of this may depend
96 /// on the first number.
97 /// }
98 /// 5. The name of the function to call.
99 /// 6. The arguments in reverse(!) order.
101 /// Notes
102 /// 1. We don't know what happens when data from another process is left in
103 /// the buffer with a timestamp. Does it ever get overwritten?
104 /// 2. The timestamp seems to be allocated when LocalConnection.send is called,
105 /// even though the message may be sent much later.
106 /// 3. We can probably stop checking the data if (a) we have nothing more to
107 /// send, (b) we are not connected, and (c) the last data was not written
108 /// by us. Gnash doesn't do the additional check for (c), so will never
109 /// remove the advance callback if data with a timestamp from another
110 /// process stays in the buffer. Note 1 also relates to this.
112 /// http://www.osflash.org/localconnection
114 /// Some facts:
115 /// * The header is 16 bytes,
116 /// * The message can be up to 40k,
117 /// * The listeners block starts at 40k+16 = 40976 bytes,
119 namespace {
120 gnash::RcInitFile& rcfile = gnash::RcInitFile::getDefaultInstance();
123 namespace gnash {
125 namespace {
126 as_value localconnection_connect(const fn_call& fn);
127 as_value localconnection_domain(const fn_call& fn);
128 as_value localconnection_send(const fn_call& fn);
129 as_value localconnection_new(const fn_call& fn);
130 as_value localconnection_close(const fn_call& fn);
132 bool validFunctionName(const std::string& func);
133 void attachLocalConnectionInterface(as_object& o);
135 std::string getDomain(as_object& o);
137 void removeListener(const std::string& name, SharedMem& mem);
138 bool addListener(const std::string& name, SharedMem& mem);
139 bool findListener(const std::string& name, SharedMem& mem);
140 void getMarker(SharedMem::iterator& i, SharedMem::iterator end);
141 void markRead(SharedMem& m);
142 inline std::uint32_t getTimestamp(const VM& vm);
144 /// Read the AMF data and invoke the function.
145 void executeAMFFunction(as_object& owner, amf::Reader& rd);
147 struct ConnectionData
149 std::string name;
150 std::uint32_t ts;
151 SimpleBuffer data;
156 void
157 writeLong(std::uint8_t*& ptr, std::uint32_t i)
159 *ptr = i & 0xff;
160 ++ptr;
161 *ptr = (i & 0xff00) >> 8;
162 ++ptr;
163 *ptr = (i & 0xff0000) >> 16;
164 ++ptr;
165 *ptr = (i & 0xff000000) >> 24;
166 ++ptr;
169 inline std::uint32_t
170 readLong(const std::uint8_t* buf) {
171 std::uint32_t s = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
172 return s;
176 /// A null byte after the marker or at the beginning of the listeners
177 /// signifies the end of the list.
178 template<typename T, size_t N>
179 std::string
180 fromByteString(T(&buf)[N])
182 return std::string(buf, buf + N - 1);
185 static const std::string marker(fromByteString("\0::3\0::4\0"));
189 /// Open a connection between two SWF movies so they can send
190 /// each other Flash Objects to be executed.
191 class LocalConnection_as : public ActiveRelay
194 public:
196 /// The size of the shared memory segment.
197 static const size_t defaultSize = 64528;
199 /// Offset of listeners in the shared memory segment.
200 static const size_t listenersOffset = 40976;
202 /// Create a LocalConnection_as object.
204 /// @param owner The as_object that owns this Relay.
205 LocalConnection_as(as_object* owner);
207 virtual ~LocalConnection_as() {}
209 /// Remove ourself as a listener (if connected).
210 void close();
212 const std::string& domain() {
213 return _domain;
216 /// Called on advance().
218 /// Handles sending and receiving.
219 virtual void update();
221 bool connected() const {
222 return _connected;
225 void connect(const std::string& name);
227 void send(std::unique_ptr<ConnectionData> d)
229 assert(d.get());
230 VM& vm = getVM(owner());
231 d->ts = getTimestamp(vm);
232 _queue.push_back(std::move(d));
234 // Register callback so we can send the data on the next advance.
235 movie_root& mr = getRoot(owner());
236 mr.addAdvanceCallback(this);
239 private:
241 std::string _name;
243 // The immutable domain of this LocalConnection_as, based on the
244 // originating SWF's domain.
245 const std::string _domain;
247 bool _connected;
249 SharedMem _shm;
251 std::deque<std::unique_ptr<ConnectionData>> _queue;
253 // The timestamp of our last write to the shared memory.
254 std::uint32_t _lastTime;
258 const size_t LocalConnection_as::listenersOffset;
259 const size_t LocalConnection_as::defaultSize;
261 LocalConnection_as::LocalConnection_as(as_object* o)
263 ActiveRelay(o),
264 _domain(getDomain(owner())),
265 _connected(false),
266 _shm(defaultSize),
267 _lastTime(0)
271 void
272 LocalConnection_as::update()
274 // Check whether local connection is disabled(!): brilliant choice of
275 // function name.
276 if (rcfile.getLocalConnection()) {
277 log_security(_("Attempting to write to disabled LocalConnection!"));
278 movie_root& mr = getRoot(owner());
279 mr.removeAdvanceCallback(this);
280 return;
283 // No-op if already attached. Nothing to do if it fails, but we
284 // should probably stop trying.
285 if (!_shm.attach()) {
286 log_error(_("Failed to attach shared memory segment"));
287 return;
290 // We need the lock to prevent simultaneous reads/writes from other
291 // processes.
292 SharedMem::Lock lock(_shm);
293 if (!lock.locked()) {
294 log_error(_("Failed to get shm lock"));
295 return;
298 SharedMem::iterator ptr = _shm.begin();
300 // First check timestamp data.
302 // These are not network byte order by default, but not sure about
303 // host byte order.
304 const std::uint32_t timestamp = readLong(ptr + 8);
305 const std::uint32_t size = readLong(ptr + 12);
307 // As long as there is a timestamp in the shared memory, we mustn't
308 // write anything.
310 // We check if this is data we are listening for. If it is, read it and
311 // mark for overwriting.
313 // If not, we keep checking until the data has been overwritten by
314 // another listener or until it's expired. If it's expired, we
315 // mark for overwriting.
316 if (timestamp) {
318 // Start after 16-byte header.
319 const std::uint8_t* b = ptr + 16;
321 // End at reported size of AMF sequence.
322 const std::uint8_t* end = b + size;
324 amf::Reader rd(b, end, getGlobal(owner()));
325 as_value a;
327 // Get the connection name. That's all we need to remove expired
328 // data.
329 if (!rd(a)) {
330 log_error(_("Invalid connection name data"));
331 return;
333 const std::string& connection = a.to_string();
335 // Now check if data we wrote has expired. There's no really
336 // reliable way of checking that we genuinely wrote it.
337 if (_lastTime == timestamp) {
339 const size_t timeout = 4 * 1000;
341 VM& vm = getVM(owner());
342 const std::uint32_t timeNow = getTimestamp(vm);
344 if (timeNow - timestamp > timeout) {
345 log_debug("Data %s expired at %s. Removing its target "
346 "as a listener", timestamp, timeNow);
347 removeListener(connection, _shm);
348 markRead(_shm);
349 _lastTime = 0;
353 // If we are listening and the data is for us, get the rest of it
354 // and call the method.
355 if (_connected && connection == _domain + ":" + _name) {
356 executeAMFFunction(owner(), rd);
357 // Zero the timestamp bytes to signal that the shared memory
358 // can be written again.
359 markRead(_shm);
361 else {
362 // The data has not expired and we didn't read it. Leave it
363 // alone until it's expired or someone else has read it.
364 return;
368 // If we have no data to send, there's nothing more to do.
369 if (_queue.empty()) {
370 // ...except remove the callback if we aren't listening for anything.
371 if (!_connected) {
372 movie_root& mr = getRoot(owner());
373 mr.removeAdvanceCallback(this);
375 return;
378 // Get the first buffer.
379 std::unique_ptr<ConnectionData> cd = std::move(_queue.front());
380 _queue.pop_front();
382 // If the correct listener isn't there, iterate until we find one or
383 // there aren't any left.
384 while (!findListener(_domain + ":" + cd->name, _shm)) {
385 if (_queue.empty()) {
386 // Make sure we send the empty header later.
387 cd->ts = 0;
388 break;
390 cd = std::move(_queue.front());
391 _queue.pop_front();
394 // Yes, there is data to send.
395 const char i[] = { 1, 0, 0, 0, 1, 0, 0, 0 };
396 std::copy(i, i + arraySize(i), ptr);
398 SimpleBuffer& buf = cd->data;
400 SharedMem::iterator tmp = ptr + 8;
401 writeLong(tmp, cd->ts);
402 writeLong(tmp, cd->ts ? buf.size() : 0);
403 std::copy(buf.data(), buf.data() + buf.size(), tmp);
405 // Note the timestamp of our last send. We will keep calling update()
406 // until the data has expired or been read.
407 _lastTime = cd->ts;
411 /// Closes the LocalConnection object.
413 /// This removes the advanceCallback (so we can be removed by the GC) and
414 /// removes this object as a listener from the shared memory listeners
415 /// section.
416 void
417 LocalConnection_as::close()
419 // We may be waiting either to send or to receive, so in both cases
420 // make sure update() isn't called again.
421 movie_root& mr = getRoot(owner());
422 mr.removeAdvanceCallback(this);
424 if (!_connected) return;
425 _connected = false;
427 SharedMem::Lock lock(_shm);
428 if (!lock.locked()) {
429 log_error(_("Failed to get lock on shared memory! Will not remove "
430 "listener"));
431 return;
434 removeListener(_domain + ":" + _name, _shm);
438 /// Makes the LocalConnection object listen.
439 ///
440 /// The name is a symbolic name like "lc1", that is used by the
441 /// send() command to signify which local connection to send the
442 /// object to.
444 /// When connect is called, this object adds its domain + name plus some
445 /// other bits of information to the listeners portion of the shared memory.
446 /// It also sets the initial bytes of the shared memory to a set
447 /// pattern.
449 /// The connection will fail if a listener with the same id (domain + name)
450 /// already exists. ActionScript isn't informed of this failure.
451 void
452 LocalConnection_as::connect(const std::string& name)
454 assert(!name.empty());
456 _name = name;
458 if (!_shm.attach()) {
459 log_error(_("Failed to open shared memory segment"));
460 return;
463 SharedMem::iterator ptr = _shm.begin();
465 // We can't connect if there is already a listener with the same name.
466 if (!addListener(_domain + ":" + _name, _shm)) {
467 return;
470 const char i[] = { 1, 0, 0, 0, 1, 0, 0, 0 };
471 std::copy(i, i + 8, ptr);
473 movie_root& mr = getRoot(owner());
474 mr.addAdvanceCallback(this);
476 _connected = true;
478 return;
481 void
482 localconnection_class_init(as_object& where, const ObjectURI& uri)
484 registerBuiltinClass(where, localconnection_new,
485 attachLocalConnectionInterface, nullptr, uri);
488 void
489 registerLocalConnectionNative(as_object& global)
491 VM& vm = getVM(global);
492 vm.registerNative(localconnection_connect, 2200, 0);
493 vm.registerNative(localconnection_send, 2200, 1);
494 vm.registerNative(localconnection_close, 2200, 2);
495 vm.registerNative(localconnection_domain, 2200, 3);
499 // Anonymous namespace for module-statics
500 namespace {
502 /// Instantiate a new LocalConnection object within a flash movie
503 as_value
504 localconnection_new(const fn_call& fn)
506 // TODO: this doesn't happen on construction.
507 as_object* obj = ensure<ValidThis>(fn);
508 obj->setRelay(new LocalConnection_as(obj));
509 return as_value();
512 /// The callback for LocalConnection::close()
513 as_value
514 localconnection_close(const fn_call& fn)
516 LocalConnection_as* relay = ensure<ThisIsNative<LocalConnection_as> >(fn);
517 relay->close();
518 return as_value();
521 /// The callback for LocalConnectiono::connect()
522 as_value
523 localconnection_connect(const fn_call& fn)
525 LocalConnection_as* relay = ensure<ThisIsNative<LocalConnection_as> >(fn);
527 // If already connected, don't try again until close() is called.
528 if (relay->connected()) return false;
530 if (!fn.nargs) {
531 IF_VERBOSE_ASCODING_ERRORS(
532 log_aserror(_("LocalConnection.connect() expects exactly "
533 "1 argument"));
535 return as_value(false);
538 if (!fn.arg(0).is_string()) {
539 IF_VERBOSE_ASCODING_ERRORS(
540 log_aserror(_("LocalConnection.connect(): first argument must "
541 "be a string"));
543 return as_value(false);
546 if (fn.arg(0).to_string().empty()) {
547 return as_value(false);
550 std::string connection = fn.arg(0).to_string();
552 relay->connect(connection);
554 // We don't care whether connected or not.
555 return as_value(true);
558 /// The callback for LocalConnection::domain()
559 as_value
560 localconnection_domain(const fn_call& fn)
562 LocalConnection_as* relay = ensure<ThisIsNative<LocalConnection_as> >(fn);
563 return as_value(relay->domain());
566 /// LocalConnection.send()
568 /// Returns false only if the call was syntactically incorrect.
570 /// The pp only ever seems have one send sequence (at least in the first 512
571 /// bytes). Subsequent sends overwrite any sequence in shared memory.
573 /// The pp sometimes sends calls with no timestamp and no length. These
574 /// appear to be ignored, so it's not clear what the point is.
575 as_value
576 localconnection_send(const fn_call& fn)
578 LocalConnection_as* relay = ensure<ThisIsNative<LocalConnection_as> >(fn);
580 // At least 2 args (connection name, function) required.
581 if (fn.nargs < 2) {
582 IF_VERBOSE_ASCODING_ERRORS(
583 std::ostringstream os;
584 fn.dump_args(os);
585 log_aserror(_("LocalConnection.send(%s): requires at least 2 "
586 "arguments"), os.str());
588 return as_value(false);
592 // Both the first two arguments must be a string
593 if (!fn.arg(0).is_string() || !fn.arg(1).is_string()) {
594 IF_VERBOSE_ASCODING_ERRORS(
595 std::ostringstream os;
596 fn.dump_args(os);
597 log_aserror(_("LocalConnection.send(%s): requires at least 2 "
598 "arguments"), os.str());
600 return as_value(false);
603 const std::string& name = fn.arg(0).to_string();
604 const std::string& func = fn.arg(1).to_string();
606 if (!validFunctionName(func)) {
607 IF_VERBOSE_ASCODING_ERRORS(
608 std::ostringstream os;
609 fn.dump_args(os);
610 log_aserror(_("LocalConnection.send(%s): requires at least 2 "
611 "arguments"), os.str());
613 return as_value(false);
616 std::unique_ptr<ConnectionData> cd(new ConnectionData());
618 SimpleBuffer& buf = cd->data;
620 // Don't know whether strict arrays are allowed
621 amf::Writer w(buf, false);
622 const std::string& domain = relay->domain();
624 w.writeString(domain + ":" + name);
625 w.writeString(domain);
626 w.writeString(func);
628 for (size_t i = fn.nargs - 1; i > 1; --i) {
629 fn.arg(i).writeAMF0(w);
632 // Now we have a valid call.
634 cd->name = name;
636 relay->send(std::move(cd));
638 return as_value(true);
642 void
643 attachLocalConnectionInterface(as_object& o)
645 VM& vm = getVM(o);
646 o.init_member("connect", vm.getNative(2200, 0));
647 o.init_member("send", vm.getNative(2200, 1));
648 o.init_member("close", vm.getNative(2200, 2));
649 o.init_member("domain", vm.getNative(2200, 3));
652 /// These names are invalid as a function name.
653 bool
654 validFunctionName(const std::string& func)
657 if (func.empty()) return false;
659 typedef std::vector<std::string> ReservedNames;
661 static const ReservedNames reserved = {
662 "send",
663 "onStatus",
664 "close",
665 "connect",
666 "domain",
667 "allowDomain"};
669 const ReservedNames::const_iterator it =
670 std::find_if(reserved.begin(), reserved.end(),
671 std::bind(StringNoCaseEqual(), std::placeholders::_1, func));
673 return (it == reserved.end());
676 // When a listener is removed, subsequent listeners are copied to the
677 // beginning. The byte after the marker is overwritten. If no listeners
678 // are left, the first byte becomes 0.
679 void
680 removeListener(const std::string& name, SharedMem& mem)
682 assert(attached(mem));
684 SharedMem::iterator ptr = mem.begin() + LocalConnection_as::listenersOffset;
686 // No listeners if the first byte is 0.
687 if (!*ptr) return;
689 SharedMem::iterator found = nullptr;
691 SharedMem::iterator next;
693 // Next should always point to the beginning of a listener.
694 while ((next = std::find(ptr, mem.end(), '\0')) != mem.end()) {
696 // Move next to where it should be (beginning of next string).
697 getMarker(next, mem.end());
699 // Check whether we've found the string (should only be once).
700 if (std::equal(name.c_str(), name.c_str() + name.size(), ptr)) {
701 found = ptr;
704 // Found last listener (or reached the end).
705 if (next == mem.end() || !*next) {
707 if (!found) return;
709 // Name and marker.
710 const ptrdiff_t size = name.size() + marker.size();
712 // Copy listeners backwards to fill in the gaps.
713 std::copy(found + size, next, found);
715 return;
718 ptr = next;
724 /// Two listeners with the same name are never added.
725 bool
726 findListener(const std::string& name, SharedMem& mem)
728 assert(attached(mem));
730 SharedMem::iterator ptr = mem.begin() + LocalConnection_as::listenersOffset;
732 SharedMem::iterator next;
734 // No listeners at all.
735 if (!*ptr) return false;
737 while ((next = std::find(ptr, mem.end(), '\0')) != mem.end()) {
739 if (std::equal(name.c_str(), name.c_str() + name.size(), ptr)) {
740 return true;
743 getMarker(next, mem.end());
745 // Found last listener.
746 if (!*next) return false;
747 ptr = next;
749 return false;
752 /// Two listeners with the same name are never added.
753 bool
754 addListener(const std::string& name, SharedMem& mem)
756 assert(attached(mem));
758 SharedMem::iterator ptr = mem.begin() + LocalConnection_as::listenersOffset;
760 SharedMem::iterator next;
762 if (!*ptr) {
763 // There are no listeners.
764 next = ptr;
766 else {
767 while ((next = std::find(ptr, mem.end(), '\0')) != mem.end()) {
769 getMarker(next, mem.end());
771 if (std::equal(name.c_str(), name.c_str() + name.size(), ptr)) {
772 log_debug("Not adding duplicated listener");
773 return false;
776 // Found last listener.
777 if (!*next) break;
778 ptr = next;
780 if (next == mem.end()) {
781 log_error(_("No space for listener in shared memory!"));
782 return false;
786 // Copy name and marker to listeners section.
787 std::string id(name + marker);
788 std::copy(id.c_str(), id.c_str() + id.size(), next);
790 // Always add an extra null after the final listener.
791 *(next + id.size()) = '\0';
793 return true;
796 /// Check whether there is a marker after the listener name and skip it.
798 /// @param i Always moved to point to the next listener string.
799 /// @param end The end of the shared memory second to read.
801 /// A marker looks like this "::3\0::4\0" or "::3\0::2\0". We don't know
802 /// what the numbers mean, or which ones are valid.
804 /// Currently this check ignores the digits.
805 void
806 getMarker(SharedMem::iterator& i, SharedMem::iterator end)
808 // i points to 0 before marker.
809 assert(*i == '\0');
810 if (i == end) return;
812 // Move to after null.
813 ++i;
815 // Then check for marker.
816 if (end - i < 8) return;
818 const char m[] = "::";
820 // Check for "::" patterns.
821 if (!std::equal(i, i + 2, m) || !std::equal(i + 4, i + 6, m)) {
822 return;
825 // Check for terminating 0.
826 if (*(i + 7) != '\0') return;
828 i += 8;
829 return;
833 /// Read the function data, call the function.
835 /// This function does not mark the data for overwriting.
836 void
837 executeAMFFunction(as_object& o, amf::Reader& rd)
839 as_value a;
841 if (!rd(a) || !a.is_string()) {
842 log_error(_("Invalid domain %s"), a);
843 return;
845 const std::string& domain = a.to_string();
846 log_debug("Domain: %s", domain);
848 if (!rd(a)) {
849 log_error(_("Invalid function name %s"), a);
850 return;
853 // This is messy and verbose because we don't know what it means.
854 // If the value after the domain is a boolean, it appears to signify a
855 // set of extra data. It's logged so that we can find exceptions more
856 // easily.
857 if (a.is_bool()) {
859 // Both bools have been false in all the examples I've seen.
860 log_debug("First bool: %s", a);
861 if (rd(a)) log_debug("Second Bool: %s", a);
863 // We guess that the first number describes the number of data fields
864 // after the second number, before the function name.
865 if (rd(a)) log_debug("First Number: %s", a);
867 // Handle negative numbers.
868 const size_t count = std::max<int>(0, toInt(a, getVM(o)));
870 // We don't know what the second number signifies.
871 if (rd(a)) log_debug("Second Number: %s", a);
873 for (size_t i = 0; i < count; ++i) {
874 if (!rd(a)) {
875 log_error(_("Fewer AMF fields than expected."));
876 return;
878 log_debug("Data: %s", a);
881 // Now we expect the next field to be the method to call.
882 if (!rd(a)) return;
885 const std::string& meth = a.to_string();
887 // These are in reverse order!
888 std::vector<as_value> d;
889 while(rd(a)) d.push_back(a);
890 std::reverse(d.begin(), d.end());
891 fn_call::Args args;
892 args.swap(d);
894 // Call the method on this LocalConnection object.
895 VM& vm = getVM(o);
896 as_function* f = getMember(o, getURI(vm, meth)).to_function();
898 invoke(f, as_environment(getVM(o)), &o, args);
901 /// Zero timestamp and length bytes to mark the data as overwritable.
902 void
903 markRead(SharedMem& m)
905 std::fill_n(m.begin() + 8, 8, 0);
908 /// Return a number usable as a timestamp.
910 /// Different players use different values here. The Linux players use:
911 /// Version 9: the time since player startup
912 /// Version 10: the system uptime.
914 /// Version 10 fails if it recieves a value outside the signed 32-bit int
915 /// range, so we surmise that there is an undocumented conversion to signed
916 /// in that player. We make sure the value never exceeds 0x7fffffff.
917 inline std::uint32_t
918 getTimestamp(const VM& vm)
920 return vm.getTime() & 0x7fffffff;
923 /// String representing the domain of the current SWF file.
925 /// This is set on construction, as it should be constant.
926 /// The domain is either the "localhost", or the hostname from the
927 /// network connection. This behaviour changed for SWF v7. Prior to v7
928 /// only the domain was returned, ie dropping off node names like
929 /// "www". As of v7, the behaviour is to return the full host
930 /// name. Gnash supports both behaviours based on the version.
931 std::string
932 getDomain(as_object& o)
935 const URL& url = getRunResources(o).streamProvider().baseURL();
937 if (url.hostname().empty()) {
938 return "localhost";
941 // Adjust the name based on the swf version. Prior to v7, the nodename part
942 // was removed. For v7 or later. the full hostname is returned. The
943 // localhost is always just the localhost.
944 if (getSWFVersion(o) > 6) {
945 return url.hostname();
948 const std::string& domain = url.hostname();
950 std::string::size_type pos;
951 pos = domain.rfind('.');
953 // If there is no '.', return the whole thing.
954 if (pos == std::string::npos) {
955 return domain;
958 pos = domain.rfind(".", pos - 1);
960 // If there is no second '.', return the whole thing.
961 if (pos == std::string::npos) {
962 return domain;
965 // Return everything after the second-to-last '.'
966 // FIXME: this must be wrong, or it would return 'org.uk' for many
967 // UK websites, and not even Adobe is that stupid. I think.
968 return domain.substr(pos + 1);
972 } // anonymous namespace
974 } // end of gnash namespace