~ubuntu-branches/ubuntu/saucy/gnash/saucy-proposed

« back to all changes in this revision

Viewing changes to libcore/asobj/NetConnection.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alexander Sack
  • Date: 2008-10-13 14:29:49 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20081013142949-f6qdvnu4mn05ltdc
Tags: 0.8.4~~bzr9980-0ubuntu1
* new upstream release 0.8.4 (LP: #240325)
* ship new lib usr/lib/gnash/libmozsdk.so.* in mozilla-plugin-gnash
  - update debian/mozilla-plugin-gnash.install
* ship new lib usr/lib/gnash/libgnashnet.so.* in gnash-common
  - update debian/gnash-common.install
* add basic debian/build_head script to build latest CVS head packages.
  - add debian/build_head
* new sound architecture requires build depend on libsdl1.2-dev
  - update debian/control
* head build script now has been completely migrated to bzr (upstream +
  ubuntu)
  - update debian/build_head
* disable kde gui until klash/qt4 has been fixed; keep kde packages as empty
  packages for now.
  - update debian/rules
  - debian/klash.install
  - debian/klash.links
  - debian/klash.manpages
  - debian/konqueror-plugin-gnash.install
* drop libkonq5-dev build dependency accordingly
  - update debian/control
* don't install headers manually anymore. gnash doesnt provide a -dev
  package after all
  - update debian/rules
* update libs installed in gnash-common; libgnashserver-*.so is not available
  anymore (removed); in turn we add the new libgnashcore-*.so
  - update debian/gnash-common.install
* use -Os for optimization and properly pass CXXFLAGS=$(CFLAGS) to configure
  - update debian/rules
* touch firefox .autoreg in postinst of mozilla plugin
  - update debian/mozilla-plugin-gnash.postinst
* link gnash in ubufox plugins directory for the plugin alternative switcher
  - add debian/mozilla-plugin-gnash.links
* suggest ubufox accordingly
  - update debian/control
* add new required build-depends on libgif-dev
  - update debian/control
* add Xb-Npp-Description and Xb-Npp-File as new plugin database meta data
  - update debian/control

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// NetConnection.cpp:  Open local connections for FLV files or URLs.
 
2
// 
 
3
//   Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
4
// 
 
5
// This program is free software; you can redistribute it and/or modify
 
6
// it under the terms of the GNU General Public License as published by
 
7
// the Free Software Foundation; either version 3 of the License, or
 
8
// (at your option) any later version.
 
9
// 
 
10
// This program is distributed in the hope that it will be useful,
 
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
// GNU General Public License for more details.
 
14
//
 
15
// You should have received a copy of the GNU General Public License
 
16
// along with this program; if not, write to the Free Software
 
17
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
18
//
 
19
 
 
20
 
 
21
#ifdef HAVE_CONFIG_H
 
22
#include "gnashconfig.h"
 
23
#endif
 
24
 
 
25
#include <iostream>
 
26
#include <string>
 
27
#include <boost/scoped_ptr.hpp>
 
28
 
 
29
// FIXME: Get rid of this crap.
 
30
#if defined(HAVE_WINSOCK_H) && !defined(__OS2__)
 
31
# include <winsock.h>
 
32
#else
 
33
#include <arpa/inet.h> // for htons
 
34
#endif
 
35
 
 
36
#include "NetConnection.h"
 
37
#include "log.h"
 
38
#include "GnashException.h"
 
39
#include "builtin_function.h"
 
40
#include "movie_root.h"
 
41
#include "Object.h" // for getObjectInterface
 
42
 
 
43
#include "StreamProvider.h"
 
44
#include "URLAccessManager.h"
 
45
#include "URL.h"
 
46
 
 
47
// for NetConnection.call()
 
48
#include "VM.h"
 
49
//#include "array.h"
 
50
#include "amf.h"
 
51
#include "SimpleBuffer.h"
 
52
#include "timers.h"
 
53
#include "namedStrings.h"
 
54
 
 
55
 
 
56
// #define GNASH_DEBUG_REMOTING
 
57
 
 
58
namespace gnash {
 
59
 
 
60
static as_value netconnection_new(const fn_call& fn);
 
61
 
 
62
/// \class NetConnection
 
63
/// \brief Opens a local connection through which you can play
 
64
/// back video (FLV) files from an HTTP address or from the local file
 
65
/// system, using curl.
 
66
NetConnection::NetConnection()
 
67
        :
 
68
        as_object(getNetConnectionInterface()),
 
69
        _callQueue(0)
 
70
{
 
71
        attachProperties();
 
72
}
 
73
 
 
74
 
 
75
/*public*/
 
76
std::string NetConnection::validateURL(const std::string& url)
 
77
{
 
78
        std::string completeUrl;
 
79
        if (_prefixUrl.size() > 0) {
 
80
                if(url.size() > 0) {
 
81
                        completeUrl += _prefixUrl + "/" + url;
 
82
                } else {
 
83
                        completeUrl += _prefixUrl;
 
84
                }
 
85
        } else {
 
86
                completeUrl += url;
 
87
        }
 
88
 
 
89
        URL uri(completeUrl, get_base_url());
 
90
 
 
91
        std::string uriStr(uri.str());
 
92
        assert(uriStr.find("://") != std::string::npos);
 
93
 
 
94
        // Check if we're allowed to open url
 
95
        if (!URLAccessManager::allow(uri)) {
 
96
                log_security(_("Gnash is not allowed to open this url: %s"), uriStr);
 
97
                return "";
 
98
        }
 
99
 
 
100
        log_debug(_("Connection to movie: %s"), uriStr);
 
101
 
 
102
        return uriStr;
 
103
}
 
104
 
 
105
/*private*/
 
106
void
 
107
NetConnection::addToURL(const std::string& url)
 
108
{
 
109
        // What is this ? It is NOT documented in the header !!
 
110
        //if (url == "null" || url == "NULL") return;
 
111
 
 
112
        // If there already is something in _prefixUrl, then we already have a url,
 
113
        // so no need to renew it. This may not correct, needs some testing.
 
114
        if (_prefixUrl.size() > 0) return;
 
115
 
 
116
        _prefixUrl += url;
 
117
}
 
118
 
 
119
 
 
120
/// \brief callback to instantiate a new NetConnection object.
 
121
/// \param fn the parameters from the Flash movie
 
122
/// \return nothing from the function call.
 
123
/// \note The return value is returned through the fn.result member.
 
124
static as_value
 
125
netconnection_new(const fn_call& /* fn */)
 
126
{
 
127
        GNASH_REPORT_FUNCTION;
 
128
 
 
129
        NetConnection *netconnection_obj = new NetConnection;
 
130
 
 
131
        return as_value(netconnection_obj);
 
132
}
 
133
 
 
134
as_value
 
135
NetConnection::connect_method(const fn_call& fn)
 
136
{
 
137
        // NOTE:
 
138
        //
 
139
        // NetConnection::connect() is *documented*, I repeat, *documented*, to require the
 
140
        // "url" argument to be NULL in AS <= 2. This is *legal* and *required*. Anything
 
141
        // other than NULL is undocumented behaviour, and I would like to know if there
 
142
        // are any movies out there relying on it. --bjacques.
 
143
 
 
144
        GNASH_REPORT_FUNCTION;
 
145
 
 
146
        boost::intrusive_ptr<NetConnection> ptr = ensureType<NetConnection>(fn.this_ptr); 
 
147
    
 
148
        if (fn.nargs < 1)
 
149
        {
 
150
                IF_VERBOSE_ASCODING_ERRORS(
 
151
                log_aserror(_("NetConnection.connect(): needs at least one argument"));
 
152
                );
 
153
                return as_value(false);
 
154
        }
 
155
 
 
156
        const as_value& url_val = fn.arg(0);
 
157
 
 
158
        // Check first arg for validity 
 
159
        if ( url_val.is_null())
 
160
        {
 
161
                // Null URL was passed. This is expected. Of course, it also makes this
 
162
                // function (and, this class) rather useless. We return true, even though
 
163
                // returning true has no meaning.
 
164
                
 
165
                return as_value(true);
 
166
        }
 
167
 
 
168
        // The remainder of this function is undocumented.
 
169
        
 
170
        if (url_val.is_undefined()) {
 
171
                IF_VERBOSE_ASCODING_ERRORS(
 
172
                log_aserror(_("NetConnection.connect(): first argument shouldn't be undefined"));
 
173
                );
 
174
                return as_value(false);
 
175
        }
 
176
 
 
177
 
 
178
        /// .. TODO: checkme ... addToURL ?? shoudnl't we attempt a connection ??
 
179
        ptr->addToURL(url_val.to_string());
 
180
 
 
181
        if ( fn.nargs > 1 )
 
182
        {
 
183
                std::stringstream ss; fn.dump_args(ss);
 
184
                log_unimpl("NetConnection.connect(%s): args after the first are not supported", ss.str());
 
185
        }
 
186
 
 
187
 
 
188
        // TODO: FIXME: should return true *or false* for RTMP connections
 
189
        return as_value(true);
 
190
}
 
191
 
 
192
 
 
193
as_value
 
194
NetConnection::addHeader_method(const fn_call& fn)
 
195
{
 
196
        boost::intrusive_ptr<NetConnection> ptr = ensureType<NetConnection>(fn.this_ptr); 
 
197
        UNUSED(ptr);
 
198
 
 
199
        log_unimpl("NetConnection.addHeader()");
 
200
        return as_value();
 
201
}
 
202
 
 
203
static boost::uint16_t
 
204
readNetworkShort(const boost::uint8_t* buf) {
 
205
        boost::uint16_t s = buf[0] << 8 | buf[1];
 
206
        return s;
 
207
}
 
208
 
 
209
static boost::uint32_t
 
210
readNetworkLong(const boost::uint8_t* buf) {
 
211
        boost::uint32_t s = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
 
212
        return s;
 
213
}
 
214
 
 
215
/// Queue of remoting calls 
 
216
//
 
217
/// This class in made to handle data and do defered processing for
 
218
/// NetConnection::call()
 
219
///
 
220
/// Usage:
 
221
///
 
222
/// pass a URL to the constructor
 
223
///
 
224
/// call enqueue with a SimpleBuffer containing an encoded AMF call. If action
 
225
/// script specified a callback function, use the optional parameters to specify
 
226
/// the identifier (which must be unique) and the callback object as an as_value
 
227
///
 
228
class AMFQueue {
 
229
private:
 
230
        NetConnection& _nc;
 
231
        static const int NCCALLREPLYMAX=200000;
 
232
 
 
233
        typedef std::map<std::string, boost::intrusive_ptr<as_object> > CallbacksMap;
 
234
        CallbacksMap callbacks;
 
235
 
 
236
        SimpleBuffer postdata;
 
237
        URL url;
 
238
        boost::scoped_ptr<IOChannel> _connection;
 
239
        SimpleBuffer reply;
 
240
        int reply_start;
 
241
        int reply_end;
 
242
        int queued_count;
 
243
        unsigned int ticker;
 
244
 
 
245
public:
 
246
        AMFQueue(NetConnection& nc, URL url)
 
247
                :
 
248
                _nc(nc),
 
249
                postdata(),
 
250
                url(url),
 
251
                _connection(0),
 
252
                reply(NCCALLREPLYMAX),
 
253
                reply_start(0),
 
254
                reply_end(0),
 
255
                queued_count(0),
 
256
                ticker(0)
 
257
        {
 
258
                // leave space for header
 
259
                postdata.append("\000\000\000\000\000\000", 6);
 
260
        }
 
261
 
 
262
        ~AMFQueue() {
 
263
                stop_ticking();
 
264
        }
 
265
        
 
266
        void enqueue(const SimpleBuffer &amf, const std::string& identifier, boost::intrusive_ptr<as_object> callback) {
 
267
                push_amf(amf);
 
268
                push_callback(identifier, callback);
 
269
                //log_aserror("NetConnection::call(): called with a non-object as the callback");
 
270
        };
 
271
        void enqueue(const SimpleBuffer &amf) {
 
272
                push_amf(amf);
 
273
        };
 
274
        
 
275
        // tick is called automatically on intervals (hopefully only between
 
276
        // actionscript frames)
 
277
        //
 
278
        // it handles all networking for NetConnection::call() and dispatches
 
279
        // callbacks when needed
 
280
        void tick() {
 
281
 
 
282
#ifdef GNASH_DEBUG_REMOTING
 
283
                log_debug("tick running");
 
284
#endif
 
285
                if(_connection)
 
286
                {
 
287
 
 
288
            VM& vm = _nc.getVM();
 
289
 
 
290
#ifdef GNASH_DEBUG_REMOTING
 
291
                        log_debug("have connection");
 
292
#endif
 
293
                        int read = _connection->readNonBlocking(reply.data() + reply_end, NCCALLREPLYMAX - reply_end);
 
294
                        if(read > 0) {
 
295
#ifdef GNASH_DEBUG_REMOTING
 
296
                                log_debug("read '%1%' bytes: %2%", read, hexify(reply.data() + reply_end, read, false));
 
297
#endif
 
298
                                reply_end += read;
 
299
                        }
 
300
 
 
301
                        // There is no way to tell if we have a whole amf reply without
 
302
                        // parsing everything
 
303
                        //
 
304
                        // The reply format has a header field which specifies the
 
305
                        // number of bytes in the reply, but potlatch sends 0xffffffff
 
306
                        // and works fine in the proprietary player
 
307
                        //
 
308
                        // For now we just wait until we have the full reply.
 
309
                        //
 
310
                        // FIXME make this parse on other conditions, including: 1) when
 
311
                        // the buffer is full, 2) when we have a "length in bytes" value
 
312
                        // thas is satisfied
 
313
 
 
314
                        if(_connection->get_error())
 
315
                        {
 
316
                                log_debug("connection is in error condition, calling NetConnection.onStatus");
 
317
                                reply_start = 0;
 
318
                                reply_end = 0;
 
319
                                //log_debug("deleting connection");
 
320
                                _connection.reset(); // reset connection before calling the callback
 
321
 
 
322
                                // FIXME: should only call NetConnection's onStatus
 
323
                                //        if the IOChannel is in error condition.
 
324
                                //        (tipically 404).
 
325
                                //        When the response is empty, just nothing happens.
 
326
                                _nc.callMethod(NSV::PROP_ON_STATUS, as_value());
 
327
 
 
328
                        }
 
329
                        else if(_connection->eof() )
 
330
                        {
 
331
                                if ( reply_end > 8)
 
332
                                {
 
333
                    std::vector<as_object*> objRefs;
 
334
 
 
335
#ifdef GNASH_DEBUG_REMOTING
 
336
                                        log_debug("hit eof");
 
337
#endif
 
338
                                        boost::int16_t si;
 
339
                                        boost::uint16_t li;
 
340
                                        boost::uint8_t *b = reply.data() + reply_start;
 
341
                                        boost::uint8_t *end = reply.data() + reply_end;
 
342
 
 
343
                                        // parse header
 
344
                                        b += 2; // skip version indicator and client id
 
345
 
 
346
                                        // NOTE: this looks much like parsing of an OBJECT_AMF0
 
347
                                        si = readNetworkShort(b); b += 2; // number of headers
 
348
                                        uint8_t headers_ok = 1;
 
349
                                        if(si != 0)
 
350
                                        {
 
351
#ifdef GNASH_DEBUG_REMOTING
 
352
                                                log_debug("NetConnection::call(): amf headers section parsing");
 
353
#endif
 
354
                                                as_value tmp;
 
355
                                                for(int i = si; i > 0; --i)
 
356
                                                {
 
357
                                                        if(b + 2 > end) {
 
358
                                                                headers_ok = 0;
 
359
                                                                break;
 
360
                                                        }
 
361
                                                        si = readNetworkShort(b); b += 2; // name length
 
362
                                                        if(b + si > end) {
 
363
                                                                headers_ok = 0;
 
364
                                                                break;
 
365
                                                        }
 
366
                                                        std::string headerName((char*)b, si); // end-b);
 
367
#ifdef GNASH_DEBUG_REMOTING
 
368
                                                        log_debug("Header name %s", headerName);
 
369
#endif
 
370
                                                        b += si;
 
371
                                                        if ( b + 5 > end ) {
 
372
                                                                headers_ok = 0;
 
373
                                                                break;
 
374
                                                        }
 
375
                                                        b += 5; // skip past bool and length long
 
376
                                                        if( !tmp.readAMF0(b, end, -1, objRefs, vm) )
 
377
                                                        {
 
378
                                                                headers_ok = 0;
 
379
                                                                break;
 
380
                                                        }
 
381
#ifdef GNASH_DEBUG_REMOTING
 
382
                                                        log_debug("Header value %s", tmp);
 
383
#endif
 
384
 
 
385
                                                        { // method call for each header
 
386
                                                          // FIXME: it seems to me that the call should happen
 
387
                                                                VM& vm = _nc.getVM();
 
388
                                                                string_table& st = vm.getStringTable();
 
389
                                                                string_table::key key = st.find(headerName);
 
390
#ifdef GNASH_DEBUG_REMOTING
 
391
                                                                log_debug("Calling NetConnection.%s(%s)", headerName, tmp);
 
392
#endif
 
393
                                                                _nc.callMethod(key, tmp);
 
394
                                                        }
 
395
                                                }
 
396
                                        }
 
397
 
 
398
                                        if(headers_ok == 1) {
 
399
 
 
400
                                                si = readNetworkShort(b); b += 2; // number of replies
 
401
 
 
402
                                                // TODO consider counting number of replies we
 
403
                                                // actually parse and doing something if it
 
404
                                                // doesn't match this value (does it matter?
 
405
                                                if(si > 0) {
 
406
                                                        // parse replies until we get a parse error or we reach the end of the buffer
 
407
                                                        while(b < end) {
 
408
                                                                if(b + 2 > end) break;
 
409
                                                                si = readNetworkShort(b); b += 2; // reply length
 
410
                                                                if(si < 11) {
 
411
                                                                        log_error("NetConnection::call(): reply message name too short");
 
412
                                                                        break;
 
413
                                                                }
 
414
                                                                if(b + si > end) break;
 
415
                                                                // TODO check that the last 9 bytes are "/onResult"
 
416
                                                                // this should either split on the 2nd / or require onResult or onStatus
 
417
                                                                std::string id(reinterpret_cast<char*>(b), si - 9);
 
418
                                                                b += si;
 
419
 
 
420
                                                                // parse past unused string in header
 
421
                                                                if(b + 2 > end) break;
 
422
                                                                si = readNetworkShort(b); b += 2; // reply length
 
423
                                                                if(b + si > end) break;
 
424
                                                                b += si;
 
425
 
 
426
                                                                // this field is supposed to hold the
 
427
                                                                // total number of bytes in the rest of
 
428
                                                                // this particular reply value, but
 
429
                                                                // openstreetmap.org (which works great
 
430
                                                                // in the adobe player) sends
 
431
                                                                // 0xffffffff. So we just ignore it
 
432
                                                                if(b + 4 > end) break;
 
433
                                                                li = readNetworkLong(b); b += 4; // reply length
 
434
 
 
435
#ifdef GNASH_DEBUG_REMOTING
 
436
                                                                log_debug("about to parse amf value");
 
437
#endif
 
438
                                                                // this updates b to point to the next unparsed byte
 
439
                                                                as_value reply_as_value;
 
440
                                                                if ( ! reply_as_value.readAMF0(b, end, -1, objRefs, vm) )
 
441
                                                                {
 
442
                                                                        log_error("parse amf failed");
 
443
                                                                        // this will happen if we get
 
444
                                                                        // bogus data, or if the data runs
 
445
                                                                        // off the end of the buffer
 
446
                                                                        // provided, or if we get data we
 
447
                                                                        // don't know how to parse
 
448
                                                                        break;
 
449
                                                                }
 
450
#ifdef GNASH_DEBUG_REMOTING
 
451
                                                                log_debug("parsed amf");
 
452
#endif
 
453
 
 
454
                                                                // update variable to show how much we've parsed
 
455
                                                                reply_start = b - reply.data();
 
456
 
 
457
                                                                // if actionscript specified a callback object, call it
 
458
                                                                boost::intrusive_ptr<as_object> callback = pop_callback(id);
 
459
                                                                if(callback) {
 
460
#ifdef GNASH_DEBUG_REMOTING
 
461
                                                                        log_debug("calling onResult callback");
 
462
#endif
 
463
                                                                        // FIXME check if above line can fail and we have to react
 
464
                                                                        callback->callMethod(NSV::PROP_ON_RESULT, reply_as_value);
 
465
#ifdef GNASH_DEBUG_REMOTING
 
466
                                                                        log_debug("callback called");
 
467
#endif
 
468
                                                                } else {
 
469
#ifdef GNASH_DEBUG_REMOTING
 
470
                                                                        log_debug("couldn't find callback object");
 
471
#endif
 
472
                                                                }
 
473
                                                        }
 
474
                                                }
 
475
                                        }
 
476
                                }
 
477
                                else
 
478
                                {
 
479
                                        log_error("Response from remoting service < 8 bytes");
 
480
                                }
 
481
 
 
482
#ifdef GNASH_DEBUG_REMOTING
 
483
                                log_debug("deleting connection");
 
484
#endif
 
485
                                _connection.reset();
 
486
                                reply_start = 0;
 
487
                                reply_end = 0;
 
488
                        }
 
489
                }
 
490
 
 
491
                if(!_connection && queued_count > 0) {
 
492
#ifdef GNASH_DEBUG_REMOTING
 
493
                        log_debug("creating connection");
 
494
#endif
 
495
                        // set the "number of bodies" header
 
496
                        (reinterpret_cast<boost::uint16_t*>(postdata.data() + 4))[0] = htons(queued_count);
 
497
                        std::string postdata_str(reinterpret_cast<char*>(postdata.data()), postdata.size());
 
498
#ifdef GNASH_DEBUG_REMOTING
 
499
                        log_debug("NetConnection.call(): encoded args from %1% calls: %2%", queued_count, hexify(postdata.data(), postdata.size(), false));
 
500
#endif
 
501
                        queued_count = 0;
 
502
                        _connection.reset(StreamProvider::getDefaultInstance().getStream(url, postdata_str).release());
 
503
                        postdata.resize(6);
 
504
#ifdef GNASH_DEBUG_REMOTING
 
505
                        log_debug("connection created");
 
506
#endif
 
507
                }
 
508
 
 
509
                if(_connection == 0 && queued_count == 0) {
 
510
#ifdef GNASH_DEBUG_REMOTING
 
511
                        log_debug("stopping ticking");
 
512
#endif
 
513
                        stop_ticking();
 
514
#ifdef GNASH_DEBUG_REMOTING
 
515
                        log_debug("ticking stopped");
 
516
#endif
 
517
                }
 
518
        };
 
519
 
 
520
        static as_value amfqueue_tick_wrapper(const fn_call& fn)
 
521
        {
 
522
                boost::intrusive_ptr<NetConnection> ptr = ensureType<NetConnection>(fn.this_ptr);
 
523
                // FIXME check if it's possible for the URL of a NetConnection to change between call()s
 
524
                ptr->_callQueue->tick();
 
525
                return as_value();
 
526
        };
 
527
 
 
528
        void markReachableResources() const
 
529
        {
 
530
                for (CallbacksMap::const_iterator i=callbacks.begin(), e=callbacks.end(); i!=e; ++i)
 
531
                {
 
532
                        i->second->setReachable();
 
533
                }
 
534
        }
 
535
 
 
536
private:
 
537
        void start_ticking() 
 
538
        {
 
539
 
 
540
                if (ticker) return;
 
541
 
 
542
                boost::intrusive_ptr<builtin_function> ticker_as = 
 
543
                        new builtin_function(&AMFQueue::amfqueue_tick_wrapper);
 
544
 
 
545
                std::auto_ptr<Timer> timer(new Timer);
 
546
                unsigned long delayMS = 50; // FIXME crank up to 50 or so
 
547
                timer->setInterval(*ticker_as, delayMS, &_nc);
 
548
                ticker = _nc.getVM().getRoot().add_interval_timer(timer, true);
 
549
        }
 
550
 
 
551
        void push_amf(const SimpleBuffer &amf) 
 
552
        {
 
553
                GNASH_REPORT_FUNCTION;
 
554
 
 
555
                postdata.append(amf.data(), amf.size());
 
556
                queued_count++;
 
557
 
 
558
                start_ticking();
 
559
        }
 
560
 
 
561
        void stop_ticking() 
 
562
        {
 
563
                if (!ticker) return;
 
564
                _nc.getVM().getRoot().clear_interval_timer(ticker);
 
565
                ticker=0;
 
566
        }
 
567
 
 
568
        void push_callback(const std::string& id, boost::intrusive_ptr<as_object> callback) {
 
569
                callbacks.insert(std::pair<std::string, boost::intrusive_ptr<as_object> >(id, callback));
 
570
        }
 
571
 
 
572
        boost::intrusive_ptr<as_object> pop_callback(std::string id)
 
573
        {
 
574
                CallbacksMap::iterator it = callbacks.find(id);
 
575
                if (it != callbacks.end()) {
 
576
                        boost::intrusive_ptr<as_object> callback = it->second;
 
577
                        //boost::intrusive_ptr<as_object> callback;
 
578
                        //callback = it.second;
 
579
                        callbacks.erase(it);
 
580
                        return callback;
 
581
                }
 
582
                else {
 
583
                        return 0;
 
584
                }
 
585
        }
 
586
};
 
587
 
 
588
as_value
 
589
NetConnection::call_method(const fn_call& fn)
 
590
{
 
591
        static int call_number = 0;
 
592
        boost::intrusive_ptr<NetConnection> ptr = ensureType<NetConnection>(fn.this_ptr); 
 
593
 
 
594
        if (fn.nargs < 1)
 
595
        {
 
596
                IF_VERBOSE_ASCODING_ERRORS(
 
597
                log_aserror(_("NetConnection.call(): needs at least one argument"));
 
598
                );
 
599
                return as_value(false); // FIXME should we return true anyway?
 
600
        }
 
601
 
 
602
        const as_value& methodName_as = fn.arg(0);
 
603
        if (!methodName_as.is_string()) {
 
604
                IF_VERBOSE_ASCODING_ERRORS(
 
605
                std::stringstream ss; fn.dump_args(ss);
 
606
                log_aserror(_("NetConnection.call(%s): first argument (methodName) must be a string"), ss.str());
 
607
                );
 
608
                return as_value(false); // FIXME should we return true anyway?
 
609
        }
 
610
 
 
611
        std::stringstream ss; fn.dump_args(ss);
 
612
#ifdef GNASH_DEBUG_REMOTING
 
613
        log_debug("NetConnection.call(%s)", ss.str());
 
614
#endif
 
615
 
 
616
        // TODO: arg(1) is the response object. let it know when data comes back
 
617
        boost::intrusive_ptr<as_object> asCallback = 0;
 
618
        if(fn.nargs > 1) {
 
619
 
 
620
                if(fn.arg(1).is_object()) {
 
621
                        asCallback = (fn.arg(1).to_object());
 
622
                }
 
623
 
 
624
                else {
 
625
            IF_VERBOSE_ASCODING_ERRORS(
 
626
                        std::stringstream ss; fn.dump_args(ss);
 
627
                        log_aserror("NetConnection::call(%s): second argument must be an object", ss.str());
 
628
                        );
 
629
                }
 
630
        }
 
631
 
 
632
        boost::scoped_ptr<SimpleBuffer> buf ( new SimpleBuffer(32) );
 
633
 
 
634
        std::string methodName = methodName_as.to_string();
 
635
 
 
636
        // junk at the top (version, client id, 0 headers, 1 body)
 
637
        // this is done by AMFQueue now: buf->append("\000\000\000\000\000\001", 6);
 
638
 
 
639
        // method name
 
640
        buf->appendNetworkShort(methodName.size());
 
641
        buf->append(methodName.c_str(), methodName.size());
 
642
 
 
643
        // client id (result number) as counted string
 
644
        // the convention seems to be / followed by a unique (ascending) number
 
645
        ++call_number;
 
646
 
 
647
        std::ostringstream os;
 
648
        os << "/" << call_number;
 
649
        const std::string callNumberString = os.str();
 
650
 
 
651
        buf->appendNetworkShort(callNumberString.size());
 
652
        buf->append(callNumberString.c_str(), callNumberString.size());
 
653
 
 
654
        size_t total_size_offset = buf->size();
 
655
        buf->append("\000\000\000\000", 4); // total size to be filled in later
 
656
 
 
657
    std::map<as_object*, size_t> offsetTable;
 
658
    VM& vm = ptr->getVM();
 
659
 
 
660
        // encode array of arguments to remote method
 
661
        buf->appendByte(amf::Element::STRICT_ARRAY_AMF0);
 
662
        buf->appendNetworkLong(fn.nargs - 2);
 
663
        if (fn.nargs > 2)
 
664
    {
 
665
                for (unsigned int i = 2; i < fn.nargs; ++i)
 
666
                {
 
667
                        const as_value& arg = fn.arg(i);
 
668
            if ( ! arg.writeAMF0(*buf, offsetTable, vm) )
 
669
            {
 
670
                log_error("Could not serialize NetConnection.call argument %d", i);
 
671
            }
 
672
                }
 
673
        }
 
674
 
 
675
        // Set the "total size" parameter.
 
676
        *(reinterpret_cast<uint32_t*>(buf->data() + total_size_offset)) = htonl(buf->size() - 4 - total_size_offset);
 
677
        
 
678
 
 
679
#ifdef GNASH_DEBUG_REMOTING
 
680
        log_debug(_("NetConnection.call(): encoded args: %s"), hexify(buf->data(), buf->size(), false));
 
681
#endif
 
682
 
 
683
        // FIXME check that ptr->_prefixURL is valid
 
684
        URL url(ptr->validateURL(std::string()));
 
685
 
 
686
 
 
687
        // FIXME check if it's possible for the URL of a NetConnection to change between call()s
 
688
        if (! ptr->_callQueue.get()) {
 
689
                ptr->_callQueue.reset(new AMFQueue(*ptr, url));
 
690
        }
 
691
 
 
692
        if (asCallback) {
 
693
                //boost::intrusive_ptr<as_object> intrusive_callback(asCallback);
 
694
#ifdef GNASH_DEBUG_REMOTING
 
695
                log_debug("calling enqueue with callback");
 
696
#endif
 
697
                ptr->_callQueue->enqueue(*buf, callNumberString, asCallback);
 
698
                //? delete asCallback;
 
699
        }
 
700
        
 
701
        else {
 
702
#ifdef GNASH_DEBUG_REMOTING
 
703
                log_debug("calling enqueue without callback");
 
704
#endif
 
705
                ptr->_callQueue->enqueue(*buf);
 
706
        }
 
707
#ifdef GNASH_DEBUG_REMOTING
 
708
        log_debug("called enqueue");
 
709
#endif
 
710
 
 
711
        return as_value();
 
712
}
 
713
 
 
714
as_value
 
715
NetConnection::close_method(const fn_call& fn)
 
716
{
 
717
        boost::intrusive_ptr<NetConnection> ptr = ensureType<NetConnection>(fn.this_ptr); 
 
718
        UNUSED(ptr);
 
719
 
 
720
        log_unimpl("NetConnection.close()");
 
721
        return as_value();
 
722
}
 
723
 
 
724
as_value
 
725
NetConnection::isConnected_getset(const fn_call& fn)
 
726
{
 
727
        boost::intrusive_ptr<NetConnection> ptr = ensureType<NetConnection>(fn.this_ptr); 
 
728
        UNUSED(ptr);
 
729
 
 
730
        if ( fn.nargs == 0 ) // getter
 
731
        {
 
732
                log_unimpl("NetConnection.isConnected get");
 
733
                return as_value();
 
734
        }
 
735
        else // setter
 
736
        {
 
737
                IF_VERBOSE_ASCODING_ERRORS(
 
738
                log_aserror("Tried to set read-only property NetConnection.isConnected");
 
739
                );
 
740
                return as_value();
 
741
        }
 
742
}
 
743
 
 
744
as_value
 
745
NetConnection::uri_getset(const fn_call& fn)
 
746
{
 
747
        boost::intrusive_ptr<NetConnection> ptr = ensureType<NetConnection>(fn.this_ptr); 
 
748
        UNUSED(ptr);
 
749
 
 
750
        if ( fn.nargs == 0 ) // getter
 
751
        {
 
752
                log_unimpl("NetConnection.uri get");
 
753
                return as_value();
 
754
        }
 
755
        else // setter
 
756
        {
 
757
                log_unimpl("NetConnection.uri set");
 
758
                return as_value();
 
759
        }
 
760
 
 
761
}
 
762
 
 
763
void
 
764
NetConnection::attachNetConnectionInterface(as_object& o)
 
765
{
 
766
        o.init_member("connect", new builtin_function(NetConnection::connect_method));
 
767
        o.init_member("addHeader", new builtin_function(NetConnection::addHeader_method));
 
768
        o.init_member("call", new builtin_function(NetConnection::call_method));
 
769
        o.init_member("close", new builtin_function(NetConnection::close_method));
 
770
 
 
771
}
 
772
 
 
773
void
 
774
NetConnection::attachProperties()
 
775
{
 
776
        init_property("isConnected", &NetConnection::isConnected_getset, &NetConnection::isConnected_getset);
 
777
        init_property("uri", &NetConnection::uri_getset, &NetConnection::uri_getset);
 
778
}
 
779
 
 
780
as_object*
 
781
NetConnection::getNetConnectionInterface()
 
782
{
 
783
 
 
784
        static boost::intrusive_ptr<as_object> o;
 
785
        if ( o == NULL )
 
786
        {
 
787
                o = new as_object(getObjectInterface());
 
788
                NetConnection::attachNetConnectionInterface(*o);
 
789
        }
 
790
 
 
791
        return o.get();
 
792
}
 
793
 
 
794
void
 
795
NetConnection::registerConstructor(as_object& global)
 
796
{
 
797
 
 
798
        // This is going to be the global NetConnection "class"/"function"
 
799
        static boost::intrusive_ptr<builtin_function> cl;
 
800
 
 
801
        if ( cl == NULL )
 
802
        {
 
803
                cl=new builtin_function(&netconnection_new, getNetConnectionInterface());
 
804
                // replicate all interface to class, to be able to access
 
805
                // all methods as static functions
 
806
                // TODO: this is probably wrong !
 
807
                NetConnection::attachNetConnectionInterface(*cl);
 
808
                     
 
809
        }
 
810
 
 
811
        // Register _global.String
 
812
        global.init_member("NetConnection", cl.get());
 
813
 
 
814
}
 
815
 
 
816
// extern (used by Global.cpp)
 
817
void netconnection_class_init(as_object& global)
 
818
{
 
819
        NetConnection::registerConstructor(global);
 
820
}
 
821
 
 
822
// here to have AMFQueue definition available
 
823
NetConnection::~NetConnection()
 
824
{
 
825
}
 
826
 
 
827
void
 
828
NetConnection::markReachableResources() const
 
829
{
 
830
        if ( _callQueue.get() ) _callQueue->markReachableResources();
 
831
        markAsObjectReachable();
 
832
}
 
833
 
 
834
 
 
835
 
 
836
} // end of gnash namespace
 
837