use ObjectURI more consistently
[gnash.git] / libcore / asobj / LocalConnection_as.cpp
blobbe2b155bbaa52efad18e4c79da36f8eb9355a03f
1 // LocalConnection.cpp: Connect two SWF movies & send objects, for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
4 // 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 <boost/shared_ptr.hpp>
23 #include <cerrno>
24 #include <cstring>
25 #include <boost/cstdint.hpp> // for boost::?int??_t
26 #include <boost/assign/list_of.hpp>
27 #include <boost/bind.hpp>
29 #include "GnashSystemIOHeaders.h"
31 #include "VM.h"
32 #include "movie_root.h"
33 #include "URL.h"
34 #include "log.h"
35 #include "fn_call.h"
36 #include "Global_as.h"
37 #include "builtin_function.h"
38 #include "NativeFunction.h"
39 #include "SharedMem.h"
40 #include "namedStrings.h"
41 #include "StringPredicates.h"
42 #include "as_value.h"
43 #include "AMFConverter.h"
44 #include "ClockTime.h"
45 #include "GnashAlgorithm.h"
46 #include "RunResources.h"
47 #include "StreamProvider.h"
50 /// From observing the behaviour of the pp, the following seem to be true.
52 /// (Behaviour may be different on other platforms).
54 /// Sending
55 ///
56 /// A sender checks the timestamp value. If it is zero, the data may be
57 /// overwritten. If it is not, we check whether we can delete it.
58 ///
59 /// Data expires after 4 seconds. Processes only delete data they (think they)
60 /// wrote. If the timestamp matches the timestamp of the last data we sent,
61 /// we assume it's our data. If it's expired, mark it for overwriting and
62 /// continue.
63 ///
64 /// We continue to check whether the data has expired as long as the data
65 /// is not marked for overwriting. No changes are made to the buffer by a
66 /// sender as long as the timestamp is there.
67 ///
68 /// Once the buffer is ready for writing, check if the correct listener is
69 /// present. If it is, send the first message in our queue and store its
70 /// timestamp. If the listener is not present, go through the queue until
71 /// a message for an existing listener is found. If none is found, the
72 /// last message in the buffer is sent with no timestamp. (It's not clear if
73 /// there's any point in sending it, but it happens).
75 /// Receiving
76 ///
77 /// A listener registers itself by adding its name to the listeners section
78 /// of the shared memory. The name is null-terminated and followed by a
79 /// further marker, which is of the form "::x\0::y\0". The x and y characters
80 /// are always numbers, e.g. ::3::4, ::3::1, ::3::2. We do not know the
81 /// significance of these numbers.
83 /// A listener merely checks whether the data has a timestamp and if the
84 /// data is intended for it (by reading the first string field). If it is,
85 /// the data is deserialized, the encoded function called, and the data
86 /// marked for deletion.
88 /// Functions are encoded in a particular order after the timestamp and length
89 /// fields:
90 /// 1. connection name (domain:connection) [string]
91 /// 2. domain [string]
92 /// {
93 /// 3. The following optional data:
94 /// [boolean] (always false?)
95 /// [boolean] (always false?)
96 /// [number] (e.g. 0, 1)
97 /// [number] (e.g. 8, 6)
98 /// 4. Sometimes the filename [string]. The presence of this may depend
99 /// on the first number.
100 /// }
101 /// 5. The name of the function to call.
102 /// 6. The arguments in reverse(!) order.
104 /// Notes
105 /// 1. We don't know what happens when data from another process is left in
106 /// the buffer with a timestamp. Does it ever get overwritten?
107 /// 2. The timestamp seems to be allocated when LocalConnection.send is called,
108 /// even though the message may be sent much later.
109 /// 3. We can probably stop checking the data if (a) we have nothing more to
110 /// send, (b) we are not connected, and (c) the last data was not written
111 /// by us. Gnash doesn't do the additional check for (c), so will never
112 /// remove the advance callback if data with a timestamp from another
113 /// process stays in the buffer. Note 1 also relates to this.
115 /// http://www.osflash.org/localconnection
117 /// Some facts:
118 /// * The header is 16 bytes,
119 /// * The message can be up to 40k,
120 /// * The listeners block starts at 40k+16 = 40976 bytes,
122 namespace {
123 gnash::RcInitFile& rcfile = gnash::RcInitFile::getDefaultInstance();
126 namespace gnash {
128 namespace {
129 as_value localconnection_connect(const fn_call& fn);
130 as_value localconnection_domain(const fn_call& fn);
131 as_value localconnection_send(const fn_call& fn);
132 as_value localconnection_new(const fn_call& fn);
133 as_value localconnection_close(const fn_call& fn);
135 bool validFunctionName(const std::string& func);
136 void attachLocalConnectionInterface(as_object& o);
138 std::string getDomain(as_object& o);
140 void removeListener(const std::string& name, SharedMem& mem);
141 bool addListener(const std::string& name, SharedMem& mem);
142 bool findListener(const std::string& name, SharedMem& mem);
143 void getMarker(SharedMem::iterator& i, SharedMem::iterator end);
144 void markRead(SharedMem& m);
145 inline boost::uint32_t getTimestamp(const VM& vm);
147 /// Read the AMF data and invoke the function.
148 void executeAMFFunction(as_object& owner, amf::Reader& rd);
150 struct ConnectionData
152 std::string name;
153 boost::uint32_t ts;
154 SimpleBuffer data;
159 void
160 writeLong(boost::uint8_t*& ptr, boost::uint32_t i)
162 *ptr = i & 0xff;
163 ++ptr;
164 *ptr = (i & 0xff00) >> 8;
165 ++ptr;
166 *ptr = (i & 0xff0000) >> 16;
167 ++ptr;
168 *ptr = (i & 0xff000000) >> 24;
169 ++ptr;
172 inline boost::uint32_t
173 readLong(const boost::uint8_t* buf) {
174 boost::uint32_t s = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
175 return s;
179 /// A null byte after the marker or at the beginning of the listeners
180 /// signifies the end of the list.
181 template<typename T, size_t N>
182 std::string
183 fromByteString(T(&buf)[N])
185 return std::string(buf, buf + N - 1);
188 static const std::string marker(fromByteString("\0::3\0::4\0"));
192 /// Open a connection between two SWF movies so they can send
193 /// each other Flash Objects to be executed.
194 class LocalConnection_as : public ActiveRelay
197 public:
199 /// The size of the shared memory segment.
200 static const size_t defaultSize = 64528;
202 /// Offset of listeners in the shared memory segment.
203 static const size_t listenersOffset = 40976;
205 /// Create a LocalConnection_as object.
207 /// @param owner The as_object that owns this Relay.
208 LocalConnection_as(as_object* owner);
210 virtual ~LocalConnection_as() {}
212 /// Remove ourself as a listener (if connected).
213 void close();
215 const std::string& domain() {
216 return _domain;
219 /// Called on advance().
221 /// Handles sending and receiving.
222 virtual void update();
224 bool connected() const {
225 return _connected;
228 void connect(const std::string& name);
230 void send(boost::shared_ptr<ConnectionData> d)
232 assert(d.get());
233 VM& vm = getVM(owner());
234 d->ts = getTimestamp(vm);
235 _queue.push_back(d);
237 // Register callback so we can send the data on the next advance.
238 movie_root& mr = getRoot(owner());
239 mr.addAdvanceCallback(this);
242 private:
244 std::string _name;
246 // The immutable domain of this LocalConnection_as, based on the
247 // originating SWF's domain.
248 const std::string _domain;
250 bool _connected;
252 SharedMem _shm;
254 std::deque<boost::shared_ptr<ConnectionData> > _queue;
256 // The timestamp of our last write to the shared memory.
257 boost::uint32_t _lastTime;
261 const size_t LocalConnection_as::listenersOffset;
262 const size_t LocalConnection_as::defaultSize;
264 LocalConnection_as::LocalConnection_as(as_object* o)
266 ActiveRelay(o),
267 _domain(getDomain(owner())),
268 _connected(false),
269 _shm(defaultSize),
270 _lastTime(0)
274 void
275 LocalConnection_as::update()
277 // Check whether local connection is disabled(!): brilliant choice of
278 // function name.
279 if (rcfile.getLocalConnection()) {
280 log_security("Attempting to write to disabled LocalConnection!");
281 movie_root& mr = getRoot(owner());
282 mr.removeAdvanceCallback(this);
283 return;
286 // No-op if already attached. Nothing to do if it fails, but we
287 // should probably stop trying.
288 if (!_shm.attach()) {
289 log_error("Failed to attach shared memory segment");
290 return;
293 // We need the lock to prevent simultaneous reads/writes from other
294 // processes.
295 SharedMem::Lock lock(_shm);
296 if (!lock.locked()) {
297 log_debug("Failed to get shm lock");
298 return;
301 SharedMem::iterator ptr = _shm.begin();
303 // First check timestamp data.
305 // These are not network byte order by default, but not sure about
306 // host byte order.
307 const boost::uint32_t timestamp = readLong(ptr + 8);
308 const boost::uint32_t size = readLong(ptr + 12);
310 // As long as there is a timestamp in the shared memory, we mustn't
311 // write anything.
313 // We check if this is data we are listening for. If it is, read it and
314 // mark for overwriting.
316 // If not, we keep checking until the data has been overwritten by
317 // another listener or until it's expired. If it's expired, we
318 // mark for overwriting.
319 if (timestamp) {
321 // Start after 16-byte header.
322 const boost::uint8_t* b = ptr + 16;
324 // End at reported size of AMF sequence.
325 const boost::uint8_t* end = b + size;
327 amf::Reader rd(b, end, getGlobal(owner()));
328 as_value a;
330 // Get the connection name. That's all we need to remove expired
331 // data.
332 if (!rd(a)) {
333 log_error("Invalid connection name data");
334 return;
336 const std::string& connection = a.to_string();
338 // Now check if data we wrote has expired. There's no really
339 // reliable way of checking that we genuinely wrote it.
340 if (_lastTime == timestamp) {
342 const size_t timeout = 4 * 1000;
344 VM& vm = getVM(owner());
345 const boost::uint32_t timeNow = getTimestamp(vm);
347 if (timeNow - timestamp > timeout) {
348 log_debug("Data %s expired at %s. Removing its target "
349 "as a listener", timestamp, timeNow);
350 removeListener(connection, _shm);
351 markRead(_shm);
352 _lastTime = 0;
356 // If we are listening and the data is for us, get the rest of it
357 // and call the method.
358 if (_connected && connection == _domain + ":" + _name) {
359 executeAMFFunction(owner(), rd);
360 // Zero the timestamp bytes to signal that the shared memory
361 // can be written again.
362 markRead(_shm);
364 else {
365 // The data has not expired and we didn't read it. Leave it
366 // alone until it's expired or someone else has read it.
367 return;
371 // If we have no data to send, there's nothing more to do.
372 if (_queue.empty()) {
373 // ...except remove the callback if we aren't listening for anything.
374 if (!_connected) {
375 movie_root& mr = getRoot(owner());
376 mr.removeAdvanceCallback(this);
378 return;
381 // Get the first buffer.
382 boost::shared_ptr<ConnectionData> cd = _queue.front();
383 _queue.pop_front();
385 // If the correct listener isn't there, iterate until we find one or
386 // there aren't any left.
387 while (!findListener(_domain + ":" + cd->name, _shm)) {
388 if (_queue.empty()) {
389 // Make sure we send the empty header later.
390 cd->ts = 0;
391 break;
393 cd = _queue.front();
394 _queue.pop_front();
397 // Yes, there is data to send.
398 const char i[] = { 1, 0, 0, 0, 1, 0, 0, 0 };
399 std::copy(i, i + arraySize(i), ptr);
401 SimpleBuffer& buf = cd->data;
403 SharedMem::iterator tmp = ptr + 8;
404 writeLong(tmp, cd->ts);
405 writeLong(tmp, cd->ts ? buf.size() : 0);
406 std::copy(buf.data(), buf.data() + buf.size(), tmp);
408 // Note the timestamp of our last send. We will keep calling update()
409 // until the data has expired or been read.
410 _lastTime = cd->ts;
414 /// Closes the LocalConnection object.
416 /// This removes the advanceCallback (so we can be removed by the GC) and
417 /// removes this object as a listener from the shared memory listeners
418 /// section.
419 void
420 LocalConnection_as::close()
422 // We may be waiting either to send or to receive, so in both cases
423 // make sure update() isn't called again.
424 movie_root& mr = getRoot(owner());
425 mr.removeAdvanceCallback(this);
427 if (!_connected) return;
428 _connected = false;
430 SharedMem::Lock lock(_shm);
431 if (!lock.locked()) {
432 log_error("Failed to get lock on shared memory! Will not remove "
433 "listener");
434 return;
437 removeListener(_domain + ":" + _name, _shm);
441 /// Makes the LocalConnection object listen.
442 ///
443 /// The name is a symbolic name like "lc1", that is used by the
444 /// send() command to signify which local connection to send the
445 /// object to.
447 /// When connect is called, this object adds its domain + name plus some
448 /// other bits of information to the listeners portion of the shared memory.
449 /// It also sets the initial bytes of the shared memory to a set
450 /// pattern.
452 /// The connection will fail if a listener with the same id (domain + name)
453 /// already exists. ActionScript isn't informed of this failure.
454 void
455 LocalConnection_as::connect(const std::string& name)
457 assert(!name.empty());
459 _name = name;
461 if (!_shm.attach()) {
462 log_error("Failed to open shared memory segment");
463 return;
466 SharedMem::iterator ptr = _shm.begin();
468 // We can't connect if there is already a listener with the same name.
469 if (!addListener(_domain + ":" + _name, _shm)) {
470 return;
473 const char i[] = { 1, 0, 0, 0, 1, 0, 0, 0 };
474 std::copy(i, i + 8, ptr);
476 movie_root& mr = getRoot(owner());
477 mr.addAdvanceCallback(this);
479 _connected = true;
481 return;
484 void
485 localconnection_class_init(as_object& where, const ObjectURI& uri)
487 registerBuiltinClass(where, localconnection_new,
488 attachLocalConnectionInterface, 0, uri);
491 void
492 registerLocalConnectionNative(as_object& global)
494 VM& vm = getVM(global);
495 vm.registerNative(localconnection_connect, 2200, 0);
496 vm.registerNative(localconnection_send, 2200, 1);
497 vm.registerNative(localconnection_close, 2200, 2);
498 vm.registerNative(localconnection_domain, 2200, 3);
502 // Anonymous namespace for module-statics
503 namespace {
505 /// Instantiate a new LocalConnection object within a flash movie
506 as_value
507 localconnection_new(const fn_call& fn)
509 // TODO: this doesn't happen on construction.
510 as_object* obj = ensure<ValidThis>(fn);
511 obj->setRelay(new LocalConnection_as(obj));
512 return as_value();
515 /// The callback for LocalConnection::close()
516 as_value
517 localconnection_close(const fn_call& fn)
519 LocalConnection_as* relay = ensure<ThisIsNative<LocalConnection_as> >(fn);
520 relay->close();
521 return as_value();
524 /// The callback for LocalConnectiono::connect()
525 as_value
526 localconnection_connect(const fn_call& fn)
528 LocalConnection_as* relay = ensure<ThisIsNative<LocalConnection_as> >(fn);
530 // If already connected, don't try again until close() is called.
531 if (relay->connected()) return false;
533 if (!fn.nargs) {
534 IF_VERBOSE_ASCODING_ERRORS(
535 log_aserror(_("LocalConnection.connect() expects exactly "
536 "1 argument"));
538 return as_value(false);
541 if (!fn.arg(0).is_string()) {
542 IF_VERBOSE_ASCODING_ERRORS(
543 log_aserror(_("LocalConnection.connect(): first argument must "
544 "be a string"));
546 return as_value(false);
549 if (fn.arg(0).to_string().empty()) {
550 return as_value(false);
553 std::string connection = fn.arg(0).to_string();
555 relay->connect(connection);
557 // We don't care whether connected or not.
558 return as_value(true);
561 /// The callback for LocalConnection::domain()
562 as_value
563 localconnection_domain(const fn_call& fn)
565 LocalConnection_as* relay = ensure<ThisIsNative<LocalConnection_as> >(fn);
566 return as_value(relay->domain());
569 /// LocalConnection.send()
571 /// Returns false only if the call was syntactically incorrect.
573 /// The pp only ever seems have one send sequence (at least in the first 512
574 /// bytes). Subsequent sends overwrite any sequence in shared memory.
576 /// The pp sometimes sends calls with no timestamp and no length. These
577 /// appear to be ignored, so it's not clear what the point is.
578 as_value
579 localconnection_send(const fn_call& fn)
581 LocalConnection_as* relay = ensure<ThisIsNative<LocalConnection_as> >(fn);
583 // At least 2 args (connection name, function) required.
584 if (fn.nargs < 2) {
585 IF_VERBOSE_ASCODING_ERRORS(
586 std::ostringstream os;
587 fn.dump_args(os);
588 log_aserror(_("LocalConnection.send(%s): requires at least 2 "
589 "arguments"), os.str());
591 return as_value(false);
595 // Both the first two arguments must be a string
596 if (!fn.arg(0).is_string() || !fn.arg(1).is_string()) {
597 IF_VERBOSE_ASCODING_ERRORS(
598 std::ostringstream os;
599 fn.dump_args(os);
600 log_aserror(_("LocalConnection.send(%s): requires at least 2 "
601 "arguments"), os.str());
603 return as_value(false);
606 const std::string& name = fn.arg(0).to_string();
607 const std::string& func = fn.arg(1).to_string();
609 if (!validFunctionName(func)) {
610 IF_VERBOSE_ASCODING_ERRORS(
611 std::ostringstream os;
612 fn.dump_args(os);
613 log_aserror(_("LocalConnection.send(%s): requires at least 2 "
614 "arguments"), os.str());
616 return as_value(false);
619 boost::shared_ptr<ConnectionData> cd(new ConnectionData());
621 SimpleBuffer& buf = cd->data;
623 // Don't know whether strict arrays are allowed
624 amf::Writer w(buf, false);
625 const std::string& domain = relay->domain();
627 w.writeString(domain + ":" + name);
628 w.writeString(domain);
629 w.writeString(func);
631 for (size_t i = fn.nargs - 1; i > 1; --i) {
632 fn.arg(i).writeAMF0(w);
635 // Now we have a valid call.
637 cd->name = name;
639 relay->send(cd);
641 return as_value(true);
645 void
646 attachLocalConnectionInterface(as_object& o)
648 VM& vm = getVM(o);
649 o.init_member("connect", vm.getNative(2200, 0));
650 o.init_member("send", vm.getNative(2200, 1));
651 o.init_member("close", vm.getNative(2200, 2));
652 o.init_member("domain", vm.getNative(2200, 3));
655 /// These names are invalid as a function name.
656 bool
657 validFunctionName(const std::string& func)
660 if (func.empty()) return false;
662 typedef std::vector<std::string> ReservedNames;
664 static const ReservedNames reserved = boost::assign::list_of
665 ("send")
666 ("onStatus")
667 ("close")
668 ("connect")
669 ("domain")
670 ("allowDomain");
672 const ReservedNames::const_iterator it =
673 std::find_if(reserved.begin(), reserved.end(),
674 boost::bind(StringNoCaseEqual(), _1, func));
676 return (it == reserved.end());
679 // When a listener is removed, subsequent listeners are copied to the
680 // beginning. The byte after the marker is overwritten. If no listeners
681 // are left, the first byte becomes 0.
682 void
683 removeListener(const std::string& name, SharedMem& mem)
685 assert(attached(mem));
687 SharedMem::iterator ptr = mem.begin() + LocalConnection_as::listenersOffset;
689 // No listeners if the first byte is 0.
690 if (!*ptr) return;
692 SharedMem::iterator found = 0;
694 SharedMem::iterator next;
696 // Next should always point to the beginning of a listener.
697 while ((next = std::find(ptr, mem.end(), '\0')) != mem.end()) {
699 // Move next to where it should be (beginning of next string).
700 getMarker(next, mem.end());
702 // Check whether we've found the string (should only be once).
703 if (std::equal(name.c_str(), name.c_str() + name.size(), ptr)) {
704 found = ptr;
707 // Found last listener (or reached the end).
708 if (next == mem.end() || !*next) {
710 if (!found) return;
712 // Name and marker.
713 const ptrdiff_t size = name.size() + marker.size();
715 // Copy listeners backwards to fill in the gaps.
716 std::copy(found + size, next, found);
718 return;
721 ptr = next;
727 /// Two listeners with the same name are never added.
728 bool
729 findListener(const std::string& name, SharedMem& mem)
731 assert(attached(mem));
733 SharedMem::iterator ptr = mem.begin() + LocalConnection_as::listenersOffset;
735 SharedMem::iterator next;
737 // No listeners at all.
738 if (!*ptr) return false;
740 while ((next = std::find(ptr, mem.end(), '\0')) != mem.end()) {
742 if (std::equal(name.c_str(), name.c_str() + name.size(), ptr)) {
743 return true;
746 getMarker(next, mem.end());
748 // Found last listener.
749 if (!*next) return false;
750 ptr = next;
752 return false;
755 /// Two listeners with the same name are never added.
756 bool
757 addListener(const std::string& name, SharedMem& mem)
759 assert(attached(mem));
761 SharedMem::iterator ptr = mem.begin() + LocalConnection_as::listenersOffset;
763 SharedMem::iterator next;
765 if (!*ptr) {
766 // There are no listeners.
767 next = ptr;
769 else {
770 while ((next = std::find(ptr, mem.end(), '\0')) != mem.end()) {
772 getMarker(next, mem.end());
774 if (std::equal(name.c_str(), name.c_str() + name.size(), ptr)) {
775 log_debug("Not adding duplicated listener");
776 return false;
779 // Found last listener.
780 if (!*next) break;
781 ptr = next;
783 if (next == mem.end()) {
784 log_error("No space for listener in shared memory!");
785 return false;
789 // Copy name and marker to listeners section.
790 std::string id(name + marker);
791 std::copy(id.c_str(), id.c_str() + id.size(), next);
793 // Always add an extra null after the final listener.
794 *(next + id.size()) = '\0';
796 return true;
799 /// Check whether there is a marker after the listener name and skip it.
801 /// @param i Always moved to point to the next listener string.
802 /// @param end The end of the shared memory second to read.
804 /// A marker looks like this "::3\0::4\0" or "::3\0::2\0". We don't know
805 /// what the numbers mean, or which ones are valid.
807 /// Currently this check ignores the digits.
808 void
809 getMarker(SharedMem::iterator& i, SharedMem::iterator end)
811 // i points to 0 before marker.
812 assert(*i == '\0');
813 if (i == end) return;
815 // Move to after null.
816 ++i;
818 // Then check for marker.
819 if (end - i < 8) return;
821 const char m[] = "::";
823 // Check for "::" patterns.
824 if (!std::equal(i, i + 2, m) || !std::equal(i + 4, i + 6, m)) {
825 return;
828 // Check for terminating 0.
829 if (*(i + 7) != '\0') return;
831 i += 8;
832 return;
836 /// Read the function data, call the function.
838 /// This function does not mark the data for overwriting.
839 void
840 executeAMFFunction(as_object& o, amf::Reader& rd)
842 as_value a;
844 if (!rd(a) || !a.is_string()) {
845 log_error("Invalid domain %s", a);
846 return;
848 const std::string& domain = a.to_string();
849 log_debug("Domain: %s", domain);
851 if (!rd(a)) {
852 log_error("Invalid function name %s", a);
853 return;
856 // This is messy and verbose because we don't know what it means.
857 // If the value after the domain is a boolean, it appears to signify a
858 // set of extra data. It's logged so that we can find exceptions more
859 // easily.
860 if (a.is_bool()) {
862 // Both bools have been false in all the examples I've seen.
863 log_debug("First bool: %s", a);
864 if (rd(a)) log_debug("Second Bool: %s", a);
866 // We guess that the first number describes the number of data fields
867 // after the second number, before the function name.
868 if (rd(a)) log_debug("First Number: %s", a);
870 // Handle negative numbers.
871 const size_t count = std::max<int>(0, toInt(a, getVM(o)));
873 // We don't know what the second number signifies.
874 if (rd(a)) log_debug("Second Number: %s", a);
876 for (size_t i = 0; i < count; ++i) {
877 if (!rd(a)) {
878 log_error("Fewer AMF fields than expected.");
879 return;
881 log_debug("Data: %s", a);
884 // Now we expect the next field to be the method to call.
885 if (!rd(a)) return;
888 const std::string& meth = a.to_string();
890 // These are in reverse order!
891 std::vector<as_value> d;
892 while(rd(a)) d.push_back(a);
893 std::reverse(d.begin(), d.end());
894 fn_call::Args args;
895 args.swap(d);
897 // Call the method on this LocalConnection object.
898 VM& vm = getVM(o);
899 as_function* f = getMember(o, getURI(vm, meth)).to_function();
901 invoke(f, as_environment(getVM(o)), &o, args);
904 /// Zero timestamp and length bytes to mark the data as overwritable.
905 void
906 markRead(SharedMem& m)
908 std::fill_n(m.begin() + 8, 8, 0);
911 /// Return a number usable as a timestamp.
913 /// Different players use different values here. The Linux players use:
914 /// Version 9: the time since player startup
915 /// Version 10: the system uptime.
917 /// Version 10 fails if it recieves a value outside the signed 32-bit int
918 /// range, so we surmise that there is an undocumented conversion to signed
919 /// in that player. We make sure the value never exceeds 0x7fffffff.
920 inline boost::uint32_t
921 getTimestamp(const VM& vm)
923 return vm.getTime() & 0x7fffffff;
926 /// String representing the domain of the current SWF file.
928 /// This is set on construction, as it should be constant.
929 /// The domain is either the "localhost", or the hostname from the
930 /// network connection. This behaviour changed for SWF v7. Prior to v7
931 /// only the domain was returned, ie dropping off node names like
932 /// "www". As of v7, the behaviour is to return the full host
933 /// name. Gnash supports both behaviours based on the version.
934 std::string
935 getDomain(as_object& o)
938 const URL& url = getRunResources(o).streamProvider().baseURL();
940 if (url.hostname().empty()) {
941 return "localhost";
944 // Adjust the name based on the swf version. Prior to v7, the nodename part
945 // was removed. For v7 or later. the full hostname is returned. The
946 // localhost is always just the localhost.
947 if (getSWFVersion(o) > 6) {
948 return url.hostname();
951 const std::string& domain = url.hostname();
953 std::string::size_type pos;
954 pos = domain.rfind('.');
956 // If there is no '.', return the whole thing.
957 if (pos == std::string::npos) {
958 return domain;
961 pos = domain.rfind(".", pos - 1);
963 // If there is no second '.', return the whole thing.
964 if (pos == std::string::npos) {
965 return domain;
968 // Return everything after the second-to-last '.'
969 // FIXME: this must be wrong, or it would return 'org.uk' for many
970 // UK websites, and not even Adobe is that stupid. I think.
971 return domain.substr(pos + 1);
975 } // anonymous namespace
977 } // end of gnash namespace