add macro so new strings get found by xgettext, so they can be translated.
[gnash.git] / libcore / asobj / XMLSocket_as.cpp
blob6d5d9c8d5d110e8a35ada6301296d55accf901cc
1 // XMLSocket_as.cpp: Network socket for data (usually XML) transfer for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
4 // 2011 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
21 #include "XMLSocket_as.h"
23 #include <boost/thread.hpp>
24 #include <boost/scoped_array.hpp>
25 #include <boost/scoped_ptr.hpp>
26 #include <string>
28 #include "namedStrings.h"
29 #include "GnashSystemFDHeaders.h"
30 #include "utility.h"
31 #include "Socket.h"
32 #include "as_function.h"
33 #include "movie_root.h"
34 #include "fn_call.h"
35 #include "Global_as.h"
36 #include "VM.h"
37 #include "NativeFunction.h"
38 #include "URLAccessManager.h"
39 #include "Global_as.h"
40 #include "log.h"
42 #undef GNASH_XMLSOCKET_DEBUG
44 namespace gnash {
46 namespace {
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
61 public:
63 typedef std::vector<std::string> MessageList;
65 XMLSocket_as(as_object* owner);
66 ~XMLSocket_as();
68 /// True only when the XMLSocket is ready for read/write.
70 /// This should always match the known state of the Socket.
71 bool ready() const {
72 return _ready;
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
81 /// connection.
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
93 /// no longer ready.
95 /// You must call close() to ensure that the Socket object is ready for
96 /// a new connection.
97 void close();
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();
105 private:
107 void checkForIncomingData();
109 /// The connection
110 Socket _socket;
112 bool _ready;
114 std::string _remainder;
119 XMLSocket_as::XMLSocket_as(as_object* owner)
121 ActiveRelay(owner),
122 _ready(false)
127 XMLSocket_as::~XMLSocket_as()
131 void
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.
138 if (!ready()) {
140 if (_socket.bad()) {
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);
146 return;
149 // Not yet ready.
150 if (!_socket.connected()) return;
152 // Connection succeeded.
153 _ready = true;
154 callMethod(&owner(), NSV::PROP_ON_CONNECT, true);
158 // Now the connection is established we can receive data.
159 checkForIncomingData();
163 bool
164 XMLSocket_as::connect(const std::string& host, boost::uint16_t port)
167 if (!URLAccessManager::allowXMLSocket(host, port)) {
168 return false;
171 _socket.connect(host, port);
173 // Start callbacks on advance.
174 getRoot(owner()).addAdvanceCallback(this);
176 return true;
179 void
180 XMLSocket_as::close()
182 getRoot(owner()).removeAdvanceCallback(this);
183 _socket.close();
184 _ready = false;
188 void
189 XMLSocket_as::checkForIncomingData()
191 assert(ready());
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.
206 buf[bytesRead] = 0;
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());
215 #endif
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) {
224 _remainder += msg;
225 break;
228 if (!_remainder.empty()) {
229 msgs.push_back(_remainder + msg);
230 ptr += msg.size() + 1;
231 _remainder.clear();
232 continue;
235 // Don't do anything if nothing is received.
236 msgs.push_back(msg);
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]);
247 #endif
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);
254 if (_socket.eof()) {
255 callMethod(&owner(), NSV::PROP_ON_CLOSE);
256 close();
257 return;
262 // XMLSocket.send doesn't return anything, so we don't need
263 // to here either.
264 void
265 XMLSocket_as::send(std::string str)
267 if (!ready()) {
268 log_error(_("XMLSocket.send(): socket not initialized"));
269 return;
272 _socket.write(str.c_str(), str.size() + 1);
274 return;
278 // extern (used by Global.cpp)
279 void
280 xmlsocket_class_init(as_object& where, const ObjectURI& uri)
282 registerBuiltinClass(where, xmlsocket_new, attachXMLSocketInterface,
283 0, uri);
286 void
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);
295 namespace {
297 // XMLSocket.connect() returns true if the initial connection was
298 // successful, false if no connection was established.
299 as_value
300 xmlsocket_connect(const fn_call& fn)
303 #ifdef GNASH_XMLSOCKET_DEBUG
304 std::stringstream ss;
305 fn.dump_args(ss);
306 log_debug(_("XMLSocket.connect(%s) called"), ss.str());
307 #endif
309 XMLSocket_as* ptr = ensure<ThisIsNative<XMLSocket_as> >(fn);
311 if (ptr->ready()) {
312 log_error(_("XMLSocket.connect() called while already "
313 "connected, ignored"));
314 return as_value(false);
317 if (fn.nargs < 2) {
318 IF_VERBOSE_ASCODING_ERRORS(
319 log_aserror(_("XMLSocket.connect() needs two arguments"));
321 // TODO: check expected return values!
322 return as_value();
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.
331 // TODO: find out.
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);
343 if (!ret) {
344 log_error(_("XMLSocket.connect(): connection failed"));
347 return as_value(ret);
351 /// XMLSocket.send()
353 /// Does not return anything.
354 as_value
355 xmlsocket_send(const fn_call& fn)
357 XMLSocket_as* ptr = ensure<ThisIsNative<XMLSocket_as> >(fn);
358 if (!fn.nargs) {
359 IF_VERBOSE_ASCODING_ERRORS(
360 log_aserror(_("XMLSocket.send() needs at least one argument"));
362 return as_value();
364 const std::string& str = fn.arg(0).to_string();
365 ptr->send(str);
366 return as_value();
370 /// XMLSocket.close()
372 /// Always returns void
373 as_value
374 xmlsocket_close(const fn_call& fn)
376 XMLSocket_as* ptr = ensure<ThisIsNative<XMLSocket_as> >(fn);
377 ptr->close();
378 return as_value();
381 as_value
382 xmlsocket_new(const fn_call& fn)
384 as_object* obj = ensure<ValidThis>(fn);
385 obj->setRelay(new XMLSocket_as(obj));
386 return as_value();
389 as_value
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();
397 fn_call::Args args;
398 args += xmlin;
400 as_value xml;
401 if (ctor) {
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);
410 return as_value();
413 void
414 attachXMLSocketInterface(as_object& o)
417 VM& vm = getVM(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
427 } // gnash namespace
429 // Local Variables:
430 // mode: C++
431 // indent-tabs-mode: t
432 // End: