Update with current status
[gnash.git] / libcore / ExternalInterface.cpp
blob8801696afca9dfd4643a4537d21188f1dce842c5
1 // ExternalInterface.cpp: ActionScript "ExternalInterface" support
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014,
4 // 2015, 2016 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.
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
21 #include "ExternalInterface.h"
23 #include <map>
24 #include <vector>
25 #include <sstream>
26 #include <boost/algorithm/string/erase.hpp>
27 #include <algorithm>
29 #ifdef SOLARIS_HOST
30 # include <sys/filio.h> // for FIONREAD
31 #endif
33 #include "GnashSystemNetHeaders.h"
34 #include "GnashSystemFDHeaders.h"
36 #include "StringPredicates.h"
37 #include "fn_call.h"
38 #include "Global_as.h"
39 #include "VM.h"
40 #include "rc.h"
41 #include "as_value.h"
42 #include "as_object.h"
43 #include "XML_as.h"
44 #include "Array_as.h"
45 #include "namedStrings.h"
46 #include "Global_as.h"
47 #include "PropertyList.h"
48 #include "movie_root.h"
49 #include "log.h"
51 namespace gnash {
53 namespace {
55 class Enumerator : public KeyVisitor
57 public:
58 Enumerator(std::vector<ObjectURI>& uris) : _uris(uris) {}
59 void operator()(const ObjectURI& u) {
60 _uris.push_back(u);
62 private:
63 std::vector<ObjectURI>& _uris;
68 /// Convert an AS object to an XML string.
69 std::string
70 ExternalInterface::_objectToXML(as_object *obj)
72 // GNASH_REPORT_FUNCTION;
74 if ( ! _visited.insert(obj).second ) {
75 return "<circular/>";
78 std::stringstream ss;
80 ss << "<object>";
82 if (obj) {
83 // Get all the properties
84 VM& vm = getVM(*obj);
85 string_table& st = vm.getStringTable();
86 typedef std::vector<ObjectURI> URIs;
87 URIs uris;
88 Enumerator en(uris);
89 obj->visitKeys(en);
90 for (URIs::const_reverse_iterator i = uris.rbegin(), e = uris.rend();
91 i != e; ++i) {
92 as_value val = getMember(*obj, *i);
93 const std::string& id = i->toString(st);
94 ss << "<property id=\"" << id << "\">";
95 ss << _toXML(val);
96 ss << "</property>";
100 ss << "</object>";
102 return ss.str();
105 /// Convert an AS object to an XML string.
106 std::string
107 ExternalInterface::_toXML(const as_value &val)
109 // GNASH_REPORT_FUNCTION;
111 std::stringstream ss;
113 if (val.is_string()) {
114 ss << "<string>" << val.to_string() << "</string>";
115 } else if (val.is_number()) {
116 ss << "<number>" << val.to_string() << "</number>";
117 } else if (val.is_undefined()) {
118 ss << "<void/>";
119 } else if (val.is_null()) {
120 ss << "<null/>";
121 // Exception isn't listed in any docs, but we'll use it for
122 // marshallExceptions.
123 } else if (val.is_exception()) {
124 ss << "<exception>" << val.to_string()<< "</exception>";
125 } else if (val.is_bool()) {
126 ss << (val.to_bool(8) ? "<true/>" : "<false/>");
127 // Function also isn't listed, but it's the only other type
128 // supported by as_value, so leaving it out doesn't seem right.
129 } else if (val.is_function()) {
130 ss << "<function>" << val.to_string() << "</function>";
131 } else if (val.is_object()) {
132 as_object *obj = val.get_object();
133 ss << _objectToXML(obj);
134 } else {
135 log_error(_("Can't convert unknown type %d"), val.to_string());
138 return ss.str();
141 std::unique_ptr<ExternalInterface::invoke_t>
142 ExternalInterface::ExternalEventCheck(int fd)
144 // GNASH_REPORT_FUNCTION;
146 std::unique_ptr<ExternalInterface::invoke_t> error;
148 if (fd > 0) {
149 int bytes = 0;
150 ioctlSocket(fd, FIONREAD, &bytes);
151 if (bytes == 0) {
152 return error;
154 log_debug("There are %d bytes in the network buffer", bytes);
155 std::unique_ptr<char[]> buffer(new char[bytes + 1]);
156 // Since we know how bytes are in the network buffer, allocate
157 // some memory to read the data.
158 // terminate incase we want to treat the data like a string.
159 buffer[bytes] = 0;
160 const int ret = ::read(fd, buffer.get(), bytes);
161 if (ret > 0) {
162 return parseInvoke(std::string(buffer.get(), ret));
166 return error;
169 // Parse the XML Invoke message, which looks like this:
171 // <invoke name="LoadMovie" returntype="xml">
172 // <arguments>
173 // <number>2</number>
174 // <string>bogus</string>
175 // </arguments>
176 // </invoke>
178 std::unique_ptr<ExternalInterface::invoke_t>
179 ExternalInterface::parseInvoke(const std::string &xml)
181 std::unique_ptr<ExternalInterface::invoke_t> invoke;
182 if (xml.empty()) {
183 return invoke;
186 invoke.reset(new ExternalInterface::invoke_t);
187 std::string::size_type start = 0;
188 std::string::size_type end;
189 std::string tag;
191 // Look for the ending > in the first part of the data for the tag
192 end = xml.find(">");
193 if (end != std::string::npos) {
194 end++; // go past the > character
195 tag = xml.substr(start, end);
196 // Look for the easy ones first
197 if (tag.substr(0, 7) == "<invoke") {
198 // extract the name of the method to invoke
199 start = tag.find("name=") + 5;
200 end = tag.find(" ", start);
201 invoke->name = tag.substr(start, end-start);
202 // Ignore any quote characters around the string
203 boost::erase_first(invoke->name, "\"");
204 boost::erase_last(invoke->name, "\"");
206 // extract the return type of the method
207 start = tag.find("returntype=") + 11;
208 end = tag.find(">", start);
209 invoke->type = tag.substr(start, end-start);
210 // Ignore any quote characters around the string
211 boost::erase_first(invoke->type, "\"");
212 boost::erase_last(invoke->type, "\"");
214 // extract the arguments to the method
215 start = xml.find("<arguments>");
216 end = xml.find("</invoke");
217 tag = xml.substr(start, end-start);
218 invoke->args = ExternalInterface::parseArguments(tag);
222 return invoke;
225 as_value
226 ExternalInterface::parseXML(const std::string &xml)
228 if (xml.empty()) {
229 return as_value();
232 std::string::size_type start = 0;
233 std::string::size_type end;
234 std::string tag;
235 as_value value;
237 // Look for the ending > in the first part of the data for the tag
238 end = xml.find(">");
239 if (end != std::string::npos) {
240 end++; // go past the > character
241 tag = xml.substr(start, end);
242 // Look for the easy ones first
243 if (tag == "<null/>") {
244 value.set_null();
245 } else if (tag == "<void/>") {
246 value.set_undefined();
247 } else if (tag == "<true/>") {
248 value.set_bool(true);
249 } else if (tag == "<false/>") {
250 value.set_bool(false);
251 } else if (tag == "<number>") {
252 start = end;
253 end = xml.find("</number>");
254 std::string str = xml.substr(start, end-start);
255 double num = strtod(str.c_str(), nullptr);
256 value.set_double(num);
257 } else if (tag == "<string>") {
258 start = end;
259 end = xml.find("</string>");
260 std::string str = xml.substr(start, end-start);
261 value.set_string(str);
265 // log_debug("Argument is: %s", value.to_string());
266 return value;
269 std::vector<as_value>
270 ExternalInterface::parseArguments(const std::string &xml)
272 // GNASH_REPORT_FUNCTION;
274 std::vector<as_value> args;
275 std::string::size_type start = 0;
276 std::string::size_type end;
278 std::string name;
279 std::string data = xml;
280 std::string tag = "<arguments>";
281 start = data.find(tag);
282 if (start != std::string::npos) {
283 data.erase(0, tag.size());
285 while (!data.empty()) {
286 // Extract the data
287 start = data.find("<", 1); // start past the opening <
288 end = data.find(">", start) + 1;
289 std::string sub = data.substr(0, end);
290 if (data == "</arguments>") {
291 break;
293 args.push_back(parseXML(sub));
294 data.erase(0, end);
297 return args;
300 // Create an Invoke message for the standalone Gnash
301 std::string
302 ExternalInterface::makeInvoke (const std::string &method,
303 const std::vector<as_value> &args)
305 std::stringstream ss;
306 std::vector<as_value>::const_iterator it;
308 ss << "<invoke name=\"" << method << "\" returntype=\"xml\">";
309 ss << "<arguments>";
310 for (it=args.begin(); it != args.end(); ++it) {
311 // Should we avoid re-serializing the same object ?
312 ss << toXML(*it);
315 ss << "</arguments>";
316 ss << "</invoke>";
318 // Add a CR on the end so the output is more readable on the other
319 // end. XL should be ignoring the CR anyway.
320 ss << std::endl;
322 return ss.str();
325 size_t
326 ExternalInterface::writeBrowser(int fd, const std::string &data)
328 if (fd > 0) {
329 return ::write(fd, data.c_str(), data.size());
332 return -1;
335 std::string
336 ExternalInterface::readBrowser(int fd)
338 std::string empty;
339 fd_set fdset;
340 struct timeval timeout;
341 int fdstatus;
342 int bytes = 0;
344 // Wait for some data from the player
345 FD_ZERO(&fdset);
346 FD_SET(fd, &fdset);
347 timeout.tv_sec = 10;
348 timeout.tv_usec = 0;
349 fdstatus = select(fd + 1, &fdset, nullptr, nullptr, &timeout);
350 if (fdstatus == 0) {
351 // Timed out, return no data
352 log_error("Host container communication timed out\n");
353 return empty;
354 } else if(fdstatus < 0) {
355 // select() failed, return no data
356 log_error("select failed on host container communication: %s",
357 std::strerror(errno));
358 return empty;
361 // Check for the size of available data
362 ioctlSocket(fd, FIONREAD, &bytes);
363 if (bytes == 0) {
364 // No more data to read (end of stream, or stream error)
365 return empty;
368 log_debug("There are %d bytes in the network buffer", bytes);
370 std::string buf(bytes, '\0');
372 const int ret = ::read(fd, &buf[0], bytes);
373 if (ret <= 0) {
374 return empty;
377 if (ret < bytes) {
378 buf.resize(ret);
381 return buf;
384 } // end of gnash namespace
386 // local Variables:
387 // mode: C++
388 // indent-tabs-mode: nil
389 // End: