1 // XMLSocket_as.cpp: Network socket for data (usually XML) transfer for Gnash.
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
4 // 2011 Free Software Foundation, Inc
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 "XMLSocket_as.h"
23 #include <boost/thread.hpp>
24 #include <boost/scoped_array.hpp>
25 #include <boost/scoped_ptr.hpp>
28 #include "namedStrings.h"
29 #include "GnashSystemFDHeaders.h"
32 #include "as_function.h"
33 #include "movie_root.h"
35 #include "Global_as.h"
37 #include "NativeFunction.h"
38 #include "URLAccessManager.h"
39 #include "Global_as.h"
42 #undef GNASH_XMLSOCKET_DEBUG
47 as_value
xmlsocket_connect(const fn_call
& fn
);
48 as_value
xmlsocket_send(const fn_call
& fn
);
49 as_value
xmlsocket_new(const fn_call
& fn
);
50 as_value
xmlsocket_close(const fn_call
& fn
);
52 // These are the event handlers called for this object
53 as_value
xmlsocket_onData(const fn_call
& fn
);
55 void attachXMLSocketInterface(as_object
& o
);
59 class XMLSocket_as
: public ActiveRelay
63 typedef std::vector
<std::string
> MessageList
;
65 XMLSocket_as(as_object
* owner
);
68 /// True only when the XMLSocket is ready for read/write.
70 /// This should always match the known state of the Socket.
75 /// Attempt a connection.
77 /// @param host The host name to connect to.
78 /// @param port The port to connect to.
79 /// @return false if the connection is not allowed, otherwise true.
80 /// Note that a return of true does not mean a successful
82 bool connect(const std::string
& host
, boost::uint16_t port
);
84 /// Send a string with a null-terminator to the socket.
86 /// Actionscript doesn't care about the result.
87 void send(std::string str
);
89 /// Close the XMLSocket
91 /// This removes the core callback and instructs the Socket to close
92 /// in case it isn't already. After close() is called, the XMLSocket is
95 /// You must call close() to ensure that the Socket object is ready for
99 /// Called on advance() when socket is connected
101 /// This handles reading of data and error checking. If the Socket is
102 /// in error, it is closed and this XMLSocket object is no longer ready.
103 virtual void update();
107 void checkForIncomingData();
114 std::string _remainder
;
119 XMLSocket_as::XMLSocket_as(as_object
* owner
)
127 XMLSocket_as::~XMLSocket_as()
132 XMLSocket_as::update()
134 // This function should never be called unless a connection is active
135 // or a connection attempt is being made.
137 // If the connection was not complete, check to see if it is.
141 // Connection failed during connect()
142 // Notify onConnect and stop callback. This means update()
143 // will not be called again until XMLSocket.connect() is invoked.
144 callMethod(&owner(), NSV::PROP_ON_CONNECT
, false);
145 getRoot(owner()).removeAdvanceCallback(this);
150 if (!_socket
.connected()) return;
152 // Connection succeeded.
154 callMethod(&owner(), NSV::PROP_ON_CONNECT
, true);
158 // Now the connection is established we can receive data.
159 checkForIncomingData();
164 XMLSocket_as::connect(const std::string
& host
, boost::uint16_t port
)
167 if (!URLAccessManager::allowXMLSocket(host
, port
)) {
171 _socket
.connect(host
, port
);
173 // Start callbacks on advance.
174 getRoot(owner()).addAdvanceCallback(this);
180 XMLSocket_as::close()
182 getRoot(owner()).removeAdvanceCallback(this);
189 XMLSocket_as::checkForIncomingData()
193 std::vector
<std::string
> msgs
;
195 const int bufSize
= 10000;
196 boost::scoped_array
<char> buf(new char[bufSize
]);
198 const size_t bytesRead
= _socket
.readNonBlocking(buf
.get(), bufSize
- 1);
200 // Return if there's no data.
201 if (!bytesRead
) return;
203 if (buf
[bytesRead
- 1] != 0) {
204 // We received a partial message, so bung
205 // a null-terminator on the end.
209 char* ptr
= buf
.get();
210 while (static_cast<size_t>(ptr
- buf
.get()) < bytesRead
) {
212 #ifdef GNASH_XMLSOCKET_DEBUG
213 log_debug(_("read: %d, this string ends: %d"), bytesRead
,
214 ptr
+ std::strlen(ptr
) - buf
.get());
217 std::string
msg(ptr
);
219 // If the string reaches to the final byte read, it's
220 // incomplete. Store it and continue. The buffer is
221 // NULL-terminated, so this cannot read past the end.
222 if (static_cast<size_t>(
223 ptr
+ std::strlen(ptr
) - buf
.get()) == bytesRead
) {
228 if (!_remainder
.empty()) {
229 msgs
.push_back(_remainder
+ msg
);
230 ptr
+= msg
.size() + 1;
235 // Don't do anything if nothing is received.
238 ptr
+= msg
.size() + 1;
241 if (msgs
.empty()) return;
243 #ifdef GNASH_XMLSOCKET_DEBUG
244 for (size_t i
= 0, e
= msgs
.size(); i
!= e
; ++i
) {
245 log_debug(_(" Message %d: %s "), i
, msgs
[i
]);
249 for (XMLSocket_as::MessageList::const_iterator it
=msgs
.begin(),
250 itEnd
=msgs
.end(); it
!= itEnd
; ++it
) {
251 callMethod(&owner(), NSV::PROP_ON_DATA
, *it
);
255 callMethod(&owner(), NSV::PROP_ON_CLOSE
);
262 // XMLSocket.send doesn't return anything, so we don't need
265 XMLSocket_as::send(std::string str
)
268 log_error(_("XMLSocket.send(): socket not initialized"));
272 _socket
.write(str
.c_str(), str
.size() + 1);
278 // extern (used by Global.cpp)
280 xmlsocket_class_init(as_object
& where
, const ObjectURI
& uri
)
282 registerBuiltinClass(where
, xmlsocket_new
, attachXMLSocketInterface
,
287 registerXMLSocketNative(as_object
& global
)
289 VM
& vm
= getVM(global
);
290 vm
.registerNative(xmlsocket_connect
, 400, 0);
291 vm
.registerNative(xmlsocket_send
, 400, 1);
292 vm
.registerNative(xmlsocket_close
, 400, 2);
297 // XMLSocket.connect() returns true if the initial connection was
298 // successful, false if no connection was established.
300 xmlsocket_connect(const fn_call
& fn
)
303 #ifdef GNASH_XMLSOCKET_DEBUG
304 std::stringstream ss
;
306 log_debug(_("XMLSocket.connect(%s) called"), ss
.str());
309 XMLSocket_as
* ptr
= ensure
<ThisIsNative
<XMLSocket_as
> >(fn
);
312 log_error(_("XMLSocket.connect() called while already "
313 "connected, ignored"));
314 return as_value(false);
318 IF_VERBOSE_ASCODING_ERRORS(
319 log_aserror(_("XMLSocket.connect() needs two arguments"));
321 // TODO: check expected return values!
325 as_value hostval
= fn
.arg(0);
326 const std::string
& host
= hostval
.to_string();
327 const double port
= toNumber(fn
.arg(1), getVM(fn
));
329 // Port numbers above 65535 are rejected always, but not port numbers below
330 // 0. It's not clear what happens with them.
332 // Other ports and hosts are checked against security policy before
333 // acceptance or rejection.
334 if (port
> std::numeric_limits
<boost::uint16_t>::max()) {
335 return as_value(false);
338 // XMLSocket.connect() returns false only if the connection is
339 // forbidden. The result of the real connection attempt is
340 // notified via onConnect().
341 const bool ret
= ptr
->connect(host
, port
);
344 log_error(_("XMLSocket.connect(): connection failed"));
347 return as_value(ret
);
353 /// Does not return anything.
355 xmlsocket_send(const fn_call
& fn
)
357 XMLSocket_as
* ptr
= ensure
<ThisIsNative
<XMLSocket_as
> >(fn
);
359 IF_VERBOSE_ASCODING_ERRORS(
360 log_aserror(_("XMLSocket.send() needs at least one argument"));
364 const std::string
& str
= fn
.arg(0).to_string();
370 /// XMLSocket.close()
372 /// Always returns void
374 xmlsocket_close(const fn_call
& fn
)
376 XMLSocket_as
* ptr
= ensure
<ThisIsNative
<XMLSocket_as
> >(fn
);
382 xmlsocket_new(const fn_call
& fn
)
384 as_object
* obj
= ensure
<ValidThis
>(fn
);
385 obj
->setRelay(new XMLSocket_as(obj
));
390 xmlsocket_onData(const fn_call
& fn
)
392 const as_value
& xmlin
= fn
.nargs
? fn
.arg(0).to_string() : as_value();
394 Global_as
& gl
= getGlobal(fn
);
395 as_function
* ctor
= getMember(gl
, NSV::CLASS_XML
).to_function();
402 xml
= constructInstance(*ctor
, fn
.env(), args
);
405 // The built-in function calls:
407 // this.onXML(new XML(src));
408 callMethod(fn
.this_ptr
, NSV::PROP_ON_XML
, xml
);
414 attachXMLSocketInterface(as_object
& o
)
418 o
.init_member("connect", vm
.getNative(400, 0));
419 o
.init_member("send", vm
.getNative(400, 1));
420 o
.init_member("close", vm
.getNative(400, 2));
422 Global_as
& gl
= getGlobal(o
);
423 o
.init_member("onData", gl
.createFunction(xmlsocket_onData
));
426 } // anonymous namespace
431 // indent-tabs-mode: t