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.
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.
15 //
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
19 //
20 
21 #include "ExternalInterface.h"
22 
23 #include <map>
24 #include <vector>
25 #include <sstream>
26 #include <boost/algorithm/string/erase.hpp>
27 #include <algorithm>
28 
29 #ifdef SOLARIS_HOST
30 # include <sys/filio.h> // for FIONREAD
31 #endif
32 
33 #include "GnashSystemNetHeaders.h"
34 #include "GnashSystemFDHeaders.h"
35 
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"
50 
51 namespace gnash {
52 
53 namespace {
54 
55 class Enumerator : public KeyVisitor
56 {
57 public:
Enumerator(std::vector<ObjectURI> & uris)58     Enumerator(std::vector<ObjectURI>& uris) : _uris(uris) {}
operator ()(const ObjectURI & u)59     void operator()(const ObjectURI& u) {
60         _uris.push_back(u);
61     }
62 private:
63     std::vector<ObjectURI>& _uris;
64 };
65 
66 }
67 
68 /// Convert an AS object to an XML string.
69 std::string
_objectToXML(as_object * obj)70 ExternalInterface::_objectToXML(as_object *obj)
71 {
72     // GNASH_REPORT_FUNCTION;
73 
74     if ( ! _visited.insert(obj).second ) {
75         return "<circular/>";
76     }
77 
78     std::stringstream ss;
79 
80     ss << "<object>";
81 
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>";
97         }
98     }
99 
100     ss << "</object>";
101 
102     return ss.str();
103 }
104 
105 /// Convert an AS object to an XML string.
106 std::string
_toXML(const as_value & val)107 ExternalInterface::_toXML(const as_value &val)
108 {
109     // GNASH_REPORT_FUNCTION;
110 
111     std::stringstream ss;
112 
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());
136     }
137 
138     return ss.str();
139 }
140 
141 std::unique_ptr<ExternalInterface::invoke_t>
ExternalEventCheck(int fd)142 ExternalInterface::ExternalEventCheck(int fd)
143 {
144 //    GNASH_REPORT_FUNCTION;
145 
146     std::unique_ptr<ExternalInterface::invoke_t> error;
147 
148     if (fd > 0) {
149         int bytes = 0;
150         ioctlSocket(fd, FIONREAD, &bytes);
151         if (bytes == 0) {
152             return error;
153         }
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));
163         }
164     }
165 
166     return error;
167 }
168 
169 // Parse the XML Invoke message, which looks like this:
170 //
171 // <invoke name="LoadMovie" returntype="xml">
172 //      <arguments>
173 //              <number>2</number>
174 //              <string>bogus</string>
175 //      </arguments>
176 // </invoke>
177 //
178 std::unique_ptr<ExternalInterface::invoke_t>
parseInvoke(const std::string & xml)179 ExternalInterface::parseInvoke(const std::string &xml)
180 {
181     std::unique_ptr<ExternalInterface::invoke_t> invoke;
182     if (xml.empty()) {
183         return invoke;
184     }
185 
186     invoke.reset(new ExternalInterface::invoke_t);
187     std::string::size_type start = 0;
188     std::string::size_type end;
189     std::string tag;
190 
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, "\"");
205 
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, "\"");
213 
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);
219         }
220     }
221 
222     return invoke;
223 }
224 
225 as_value
parseXML(const std::string & xml)226 ExternalInterface::parseXML(const std::string &xml)
227 {
228     if (xml.empty()) {
229         return as_value();
230     }
231 
232     std::string::size_type start = 0;
233     std::string::size_type end;
234     std::string tag;
235     as_value value;
236 
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);
262         }
263     }
264 
265 //    log_debug("Argument is: %s", value.to_string());
266     return value;
267 }
268 
269 std::vector<as_value>
parseArguments(const std::string & xml)270 ExternalInterface::parseArguments(const std::string &xml)
271 {
272     // GNASH_REPORT_FUNCTION;
273 
274     std::vector<as_value> args;
275     std::string::size_type start = 0;
276     std::string::size_type end;
277 
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());
284     }
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;
292         }
293         args.push_back(parseXML(sub));
294         data.erase(0, end);
295     }
296 
297     return args;
298 }
299 
300 // Create an Invoke message for the standalone Gnash
301 std::string
makeInvoke(const std::string & method,const std::vector<as_value> & args)302 ExternalInterface::makeInvoke (const std::string &method,
303                                const std::vector<as_value> &args)
304 {
305     std::stringstream ss;
306     std::vector<as_value>::const_iterator it;
307 
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);
313     }
314 
315     ss << "</arguments>";
316     ss << "</invoke>";
317 
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;
321 
322     return ss.str();
323 }
324 
325 size_t
writeBrowser(int fd,const std::string & data)326 ExternalInterface::writeBrowser(int fd, const std::string &data)
327 {
328     if (fd > 0) {
329         return ::write(fd, data.c_str(), data.size());
330     }
331 
332     return -1;
333 }
334 
335 std::string
readBrowser(int fd)336 ExternalInterface::readBrowser(int fd)
337 {
338     std::string empty;
339     fd_set fdset;
340     struct timeval timeout;
341     int fdstatus;
342     int bytes = 0;
343 
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;
359     }
360 
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;
366     }
367 
368     log_debug("There are %d bytes in the network buffer", bytes);
369 
370     std::string buf(bytes, '\0');
371 
372     const int ret = ::read(fd, &buf[0], bytes);
373     if (ret <= 0) {
374         return empty;
375     }
376 
377     if (ret < bytes) {
378         buf.resize(ret);
379     }
380 
381     return buf;
382 }
383 
384 } // end of gnash namespace
385 
386 // local Variables:
387 // mode: C++
388 // indent-tabs-mode: nil
389 // End:
390