~ubuntu-branches/ubuntu/maverick/gnash/maverick

« back to all changes in this revision

Viewing changes to libcore/asobj/SharedObject_as.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Micah Gersten, Micah Gersten, Chris Coulson
  • Date: 2010-09-28 23:38:37 UTC
  • mfrom: (1.1.14 upstream) (3.1.9 sid)
  • Revision ID: james.westby@ubuntu.com-20100928233837-wcay0dodera1c7sz
Tags: 0.8.8-5ubuntu1
[ Micah Gersten <micahg@ubuntu.com> ]
* FFe - LP: #636667
* Merge from debian unstable.  Remaining changes:
  + Add Ubuntu flash alternatives in postinst and prerm
    - update debian/browser-plugin-gnash.postinst
    - update debian/browser-plugin-gnash.prerm

[ Chris Coulson <chris.coulson@canonical.com> ]
* Ensure the directories we are installing alternatives too exist
  already
  - add debian/browser-plugin-gnash.dirs

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// SharedObject_as.cpp:  ActionScript "SharedObject" class, for Gnash.
 
2
// 
 
3
//   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
 
4
//   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
#ifdef HAVE_CONFIG_H
 
22
#include "gnashconfig.h" 
 
23
#endif
 
24
 
 
25
#include "smart_ptr.h" // GNASH_USE_GC
 
26
#include "movie_root.h"
 
27
#include "GnashSystemNetHeaders.h"
 
28
#include "GnashFileUtilities.h" // stat
 
29
#include "SimpleBuffer.h"
 
30
#include "as_value.h"
 
31
#include "SharedObject_as.h"
 
32
#include "as_object.h" // for inheritance
 
33
#include "log.h"
 
34
#include "fn_call.h"
 
35
#include "Global_as.h"
 
36
#include "builtin_function.h" // need builtin_function
 
37
#include "NativeFunction.h" 
 
38
#include "VM.h"
 
39
#include "Property.h"
 
40
#include "string_table.h"
 
41
#include "rc.h" // for use of rcfile
 
42
#include "URLAccessManager.h"
 
43
#include "URL.h"
 
44
#include "NetConnection_as.h"
 
45
#include "Object.h"
 
46
#include "AMFConverter.h"
 
47
#include "GnashAlgorithm.h"
 
48
#include "RunResources.h"
 
49
#include "namedStrings.h"
 
50
 
 
51
#include <boost/scoped_array.hpp>
 
52
#include <boost/shared_ptr.hpp>
 
53
#include <cstdio>
 
54
 
 
55
namespace {
 
56
    gnash::RcInitFile& rcfile = gnash::RcInitFile::getDefaultInstance();
 
57
}
 
58
 
 
59
 
 
60
namespace gnash {
 
61
 
 
62
// Forward declarations
 
63
namespace {
 
64
 
 
65
    as_value sharedobject_connect(const fn_call& fn);
 
66
    as_value sharedobject_send(const fn_call& fn);
 
67
    as_value sharedobject_flush(const fn_call& fn);
 
68
    as_value sharedobject_close(const fn_call& fn);
 
69
    as_value sharedobject_getSize(const fn_call& fn);
 
70
    as_value sharedobject_setFps(const fn_call& fn);
 
71
    as_value sharedobject_clear(const fn_call& fn);
 
72
    as_value sharedobject_deleteAll(const fn_call& fn);
 
73
    as_value sharedobject_getDiskUsage(const fn_call& fn);
 
74
    as_value sharedobject_getRemote(const fn_call& fn);
 
75
    as_value sharedobject_data(const fn_call& fn);
 
76
    as_value sharedobject_getLocal(const fn_call& fn);
 
77
    as_value sharedobject_ctor(const fn_call& fn);
 
78
 
 
79
    as_object* readSOL(VM& vm, const std::string& filespec);
 
80
 
 
81
    /// Encode the SharedObject data.
 
82
    //
 
83
    /// @param name     The name of the SharedObject.
 
84
    /// @param data     The data object to encode.
 
85
    /// @param buf      The SimpleBuffer to encode the data to.
 
86
    bool encodeData(const std::string& name, as_object& data,
 
87
            SimpleBuffer& buf);
 
88
 
 
89
    /// Encode the 2 header bytes and data length field.
 
90
    //
 
91
    /// @param size     The genuine size of the object data.
 
92
    /// @param buf      The SimpleBuffer to encode the data to.
 
93
    void encodeHeader(const size_t size, SimpleBuffer& buf);
 
94
 
 
95
    void attachSharedObjectInterface(as_object& o);
 
96
    void attachSharedObjectStaticInterface(as_object& o);
 
97
    void flushSOL(SharedObjectLibrary::SoLib::value_type& sol);
 
98
    bool validateName(const std::string& solName);
 
99
 
 
100
    SharedObject_as* createSharedObject(Global_as& gl);
 
101
}
 
102
 
 
103
// Serializer helper
 
104
namespace { 
 
105
 
 
106
/// Class used to serialize properties of an object to a buffer in SOL format
 
107
class SOLPropsBufSerializer : public AbstractPropertyVisitor
 
108
{
 
109
 
 
110
public:
 
111
 
 
112
    SOLPropsBufSerializer(amf::Writer w, string_table& st)
 
113
        :
 
114
        _writer(w),
 
115
        _st(st),
 
116
        _error(false),
 
117
        _count(0)
 
118
        {}
 
119
    
 
120
    /// Check if the object data was successfully encoded.
 
121
    //
 
122
    /// Success means that no encoding errors were encountered and at least one
 
123
    /// property was encoded.
 
124
    bool success() const {
 
125
        return !_error && _count;
 
126
    }
 
127
 
 
128
    /// Visitor called for each property to serialize.
 
129
    //
 
130
    /// SOL serialization calls this for all existing properties, whether
 
131
    /// visible or not.
 
132
    //
 
133
    /// accept() returns false if the visitor should stop, true if it should
 
134
    /// go on to the next property.
 
135
    virtual bool accept(const ObjectURI& uri, const as_value& val) 
 
136
    {
 
137
        assert(!_error);
 
138
 
 
139
        if (val.is_function()) {
 
140
            log_debug("SOL: skip serialization of FUNCTION property");
 
141
            return true;
 
142
        }
 
143
 
 
144
        const string_table::key key = getName(uri);
 
145
 
 
146
        // Test conducted with AMFPHP:
 
147
        // '__proto__' and 'constructor' members of an object are not returned
 
148
        // from an 'echo-service'.
 
149
        // Dunno if they are not serialized or just not sent back.
 
150
        // A '__constructor__' member is returned, but only if 
 
151
        // not a function; no functions are returned at all.
 
152
        if (key == NSV::PROP_uuPROTOuu || key == NSV::PROP_CONSTRUCTOR) {
 
153
            return true;
 
154
        }
 
155
 
 
156
        // write property name
 
157
        const std::string& name = _st.value(key);
 
158
        
 
159
        _writer.writePropertyName(name);
 
160
 
 
161
        // Strict array are never encoded in SharedObject
 
162
        if (!val.writeAMF0(_writer)) {
 
163
            log_error("Problems serializing an object's member %s=%s",
 
164
                    name, val);
 
165
            _error = true;
 
166
 
 
167
            // Stop visiting.
 
168
            return false;
 
169
        }
 
170
 
 
171
        // This is SOL specific.
 
172
        boost::uint8_t end(0);
 
173
        _writer.writeData(&end, 1);
 
174
        ++_count;
 
175
        return true;
 
176
    }
 
177
 
 
178
private:
 
179
 
 
180
    amf::Writer _writer;
 
181
 
 
182
    /// String table for looking up property names as strings.
 
183
    string_table& _st;
 
184
 
 
185
    /// Whether an error has been encountered.
 
186
    bool _error;
 
187
 
 
188
    /// How many properties have been encoded.
 
189
    //
 
190
    /// A boolean would do just as nicely, but we may want to use the count
 
191
    /// info.
 
192
    size_t _count;
 
193
};
 
194
 
 
195
} // anonymous namespace
 
196
 
 
197
class SharedObject_as : public Relay
 
198
{
 
199
public:
 
200
 
 
201
    SharedObject_as(as_object& owner)
 
202
        :
 
203
        _owner(owner),
 
204
        _data(0),
 
205
        _connected(false)
 
206
    { 
 
207
    }
 
208
 
 
209
    virtual ~SharedObject_as();
 
210
 
 
211
    /// The as_object that owns this Relay.
 
212
    as_object& owner() {
 
213
        return _owner;
 
214
    }
 
215
 
 
216
    /// Write the data as a SOL file.
 
217
    //
 
218
    /// If there is no data to write, the file is removed.
 
219
    bool flush(int space = 0) const;
 
220
 
 
221
    /// The filename of this SharedObject.
 
222
    const std::string& getFilespec() const {
 
223
        return _filename;
 
224
    }
 
225
 
 
226
    /// Set the filename of the SharedObject.
 
227
    void setFilespec(const std::string& s) {
 
228
        _filename = s;
 
229
    }
 
230
 
 
231
    /// Set the name of this SharedObject.
 
232
    void setObjectName(const std::string& s) {
 
233
        _name = s;
 
234
    }
 
235
 
 
236
    /// Return the size of the data plus header.
 
237
    size_t size() const {
 
238
        if (!_data) return 0;
 
239
        SimpleBuffer buf;
 
240
 
 
241
        // The header comprises 2 bytes and a length field of 4 bytes.
 
242
        if (encodeData(_name, *_data, buf)) {
 
243
            return buf.size() + 6;
 
244
        }
 
245
        return 0;
 
246
    }
 
247
 
 
248
    /// Set the data object.
 
249
    //
 
250
    /// It seems much cleaner not to implement this as a getter/setter as it
 
251
    /// currently is, but it is shown that flush() does not call data if
 
252
    /// data is a getter-setter. This suggests that flush does not access
 
253
    /// the data object directly, which in turn suggests it's not a real
 
254
    /// property. This is the only test that fails if the data object is a
 
255
    /// simple object property.
 
256
    void setData(as_object* data) {
 
257
 
 
258
        assert(data);
 
259
        _data = data;
 
260
 
 
261
        const int flags = PropFlags::dontDelete |
 
262
                          PropFlags::readOnly;
 
263
 
 
264
        _owner.init_property(NSV::PROP_DATA, &sharedobject_data,
 
265
                &sharedobject_data, flags);
 
266
 
 
267
    }
 
268
 
 
269
    /// Get the data object.
 
270
    as_object* data() const {
 
271
        return _data;
 
272
    }
 
273
 
 
274
    /// Close the SharedObject
 
275
    //
 
276
    /// Note that this currently does nothing.
 
277
    void close();
 
278
 
 
279
    /// Are we connected? 
 
280
    bool connected() const { return _connected; }
 
281
 
 
282
    /// Override from Relay.
 
283
    virtual void setReachable(); 
 
284
 
 
285
private:
 
286
 
 
287
    /// The as_object to which this Relay belongs.
 
288
    as_object& _owner;
 
289
 
 
290
    /// An object to store and access the actual data.
 
291
    as_object* _data;
 
292
 
 
293
    std::string _name;
 
294
 
 
295
    std::string _filename;
 
296
 
 
297
    /// Are we connected? (No).
 
298
    bool _connected;
 
299
 
 
300
};
 
301
 
 
302
 
 
303
SharedObject_as::~SharedObject_as()
 
304
{
 
305
}
 
306
 
 
307
 
 
308
/// Returns false if the data cannot be written to file.
 
309
//
 
310
/// If there is no data, the file is removed and the function returns true.
 
311
bool
 
312
SharedObject_as::flush(int space) const
 
313
{
 
314
 
 
315
    /// This is called on on destruction of the SharedObject, or (allegedly)
 
316
    /// on a call to SharedObject.data, so _data is not guaranteed to exist.
 
317
    //
 
318
    /// The function should never be called from SharedObject.flush() when
 
319
    /// _data is 0.
 
320
    if (!_data) return false;
 
321
 
 
322
    if (space > 0) {
 
323
        log_unimpl("SharedObject.flush() called with a minimum disk space "
 
324
                "argument (%d), which is currently ignored", space);
 
325
    }
 
326
 
 
327
    const std::string& filespec = getFilespec();
 
328
 
 
329
    if (!mkdirRecursive(filespec)) {
 
330
        log_error("Couldn't create dir for flushing SharedObject %s", filespec);
 
331
        return false;
 
332
    }
 
333
 
 
334
#ifdef USE_SOL_READONLY
 
335
    log_debug(_("SharedObject %s not flushed (compiled as read-only mode)"),
 
336
            filespec);
 
337
    return false;
 
338
#endif
 
339
 
 
340
    if (rcfile.getSOLReadOnly()) {
 
341
        log_security("Attempting to write object %s when it's SOL "
 
342
                "Read Only is set! Refusing...", filespec);
 
343
        return false;
 
344
    }
 
345
    
 
346
    // Open file
 
347
    std::ofstream ofs(filespec.c_str(), std::ios::binary);
 
348
    if (!ofs) {
 
349
        log_error("SharedObject::flush(): Failed opening file '%s' in "
 
350
                "binary mode", filespec.c_str());
 
351
        return false;
 
352
    }
 
353
 
 
354
    // Encode data part.
 
355
    SimpleBuffer buf;
 
356
    if (!encodeData(_name, *_data, buf)) {
 
357
        std::remove(filespec.c_str());
 
358
        return true;
 
359
    }
 
360
 
 
361
    // Encode header part.
 
362
    SimpleBuffer header;
 
363
    encodeHeader(buf.size(), header);
 
364
    
 
365
    // Write header
 
366
    ofs.write(reinterpret_cast<const char*>(header.data()), header.size());
 
367
    if (!ofs) {
 
368
        log_error("Error writing SOL header");
 
369
        return false;
 
370
    }
 
371
 
 
372
    // Write AMF data
 
373
    ofs.write(reinterpret_cast<const char*>(buf.data()), buf.size());
 
374
    if (!ofs) {
 
375
        log_error("Error writing %d bytes to output file %s",
 
376
                buf.size(), filespec.c_str());
 
377
        return false;
 
378
    }
 
379
    ofs.close();
 
380
 
 
381
    log_security("SharedObject '%s' written to filesystem.", filespec);
 
382
    return true;
 
383
}
 
384
 
 
385
void
 
386
SharedObject_as::close()
 
387
{
 
388
}
 
389
 
 
390
SharedObjectLibrary::SharedObjectLibrary(VM& vm)
 
391
    :
 
392
    _vm(vm)
 
393
{
 
394
 
 
395
    _solSafeDir = rcfile.getSOLSafeDir();
 
396
    if (_solSafeDir.empty()) {
 
397
        log_debug("Empty SOLSafeDir directive: we'll use '/tmp'");
 
398
        _solSafeDir = "/tmp/";
 
399
    }
 
400
 
 
401
    // Check if the base dir exists here
 
402
    struct stat statbuf;
 
403
    if (stat(_solSafeDir.c_str(), &statbuf) == -1) {
 
404
       log_debug("Invalid SOL safe dir %s: %s. Will try to create on "
 
405
               "flush/exit.", _solSafeDir, std::strerror(errno));
 
406
    }
 
407
 
 
408
    // Which URL we should use here is under research.
 
409
    // The reference player uses the URL from which definition
 
410
    // of the call to SharedObject.getLocal was parsed.
 
411
    //
 
412
    // There is in Gnash support for tracking action_buffer 
 
413
    // urls but not yet an interface to fetch it from fn_call;
 
414
    // also, it's not clear how good would the model be (think
 
415
    // of movie A loading movie B creating the SharedObject).
 
416
    //
 
417
    // What we'll do for now is use the URL of the initially
 
418
    // loaded SWF, so that in the A loads B scenario above the
 
419
    // domain would be the one of A, not B.
 
420
    //
 
421
    // NOTE: using the base url RunResources::baseURL() would mean
 
422
    // blindly trusting the SWF publisher as base url is changed
 
423
    // by the 'base' attribute of OBJECT or EMBED tags trough
 
424
    // -P base=xxx
 
425
    const movie_root& mr = _vm.getRoot();
 
426
    const std::string& swfURL = mr.getOriginalURL();
 
427
 
 
428
    URL url(swfURL);
 
429
 
 
430
    // Remember the hostname of our SWF URL. This can be empty if loaded
 
431
    // from the filesystem
 
432
    _baseDomain = url.hostname();
 
433
 
 
434
    const std::string& urlPath = url.path();
 
435
 
 
436
    // Get the path part. If loaded from the filesystem, the pp stupidly
 
437
    // removes the first directory.
 
438
    if (!_baseDomain.empty()) {
 
439
        _basePath = urlPath;
 
440
    }
 
441
    else if (!urlPath.empty()) {
 
442
        // _basePath should be empty if there are no slashes or just one.
 
443
        std::string::size_type pos = urlPath.find('/', 1);
 
444
        if (pos != std::string::npos) {
 
445
            _basePath = urlPath.substr(pos);
 
446
        }
 
447
    }
 
448
 
 
449
}
 
450
 
 
451
void
 
452
SharedObject_as::setReachable() 
 
453
{
 
454
    _owner.setReachable();
 
455
    if (_data) _data->setReachable();
 
456
}
 
457
 
 
458
void
 
459
SharedObjectLibrary::markReachableResources() const
 
460
{
 
461
    foreachSecond(_soLib.begin(), _soLib.end(),
 
462
                  &SharedObject_as::setReachable);
 
463
}
 
464
 
 
465
/// The SharedObjectLibrary keeps all known SharedObjects alive. They must
 
466
/// be flushed on clear(). This is called at the latest by the dtor, which
 
467
/// is called at the latest by VM's dtor (currently earlier to avoid problems
 
468
/// with the GC).
 
469
void
 
470
SharedObjectLibrary::clear()
 
471
{
 
472
    std::for_each(_soLib.begin(), _soLib.end(), &flushSOL);
 
473
    _soLib.clear();
 
474
}
 
475
 
 
476
SharedObjectLibrary::~SharedObjectLibrary()
 
477
{
 
478
    clear();
 
479
}
 
480
 
 
481
as_object*
 
482
SharedObjectLibrary::getLocal(const std::string& objName,
 
483
        const std::string& root)
 
484
{
 
485
    assert (!objName.empty());
 
486
 
 
487
    // already warned about it at construction time
 
488
    if (_solSafeDir.empty()) return 0;
 
489
 
 
490
    if (rcfile.getSOLLocalDomain() && !_baseDomain.empty()) 
 
491
    {
 
492
        log_security("Attempting to open SOL file from non "
 
493
                "localhost-loaded SWF");
 
494
        return 0;
 
495
    }
 
496
 
 
497
    // Check that the name is valid; if not, return null
 
498
    if (!validateName(objName)) return 0;
 
499
 
 
500
    // The 'root' argument, otherwise known as localPath, specifies where
 
501
    // in the SWF path the SOL should be stored. It cannot be outside this
 
502
    // path.
 
503
    std::string requestedPath;
 
504
 
 
505
    // If a root is specified, check it first for validity
 
506
    if (!root.empty()) {
 
507
 
 
508
        const movie_root& mr = _vm.getRoot();
 
509
        const std::string& swfURL = mr.getOriginalURL();
 
510
        // The specified root may or may not have a domain. If it doesn't,
 
511
        // this constructor will add the SWF's domain.
 
512
        URL localPath(root, swfURL);
 
513
        
 
514
        StringNoCaseEqual noCaseCompare;
 
515
 
 
516
        // All we care about is whether the domains match. They may be 
 
517
        // empty filesystem-loaded.
 
518
        if (!noCaseCompare(localPath.hostname(), _baseDomain)) {
 
519
            log_security(_("SharedObject path %s is outside the SWF domain "
 
520
                        "%s. Cannot access this object."), localPath, 
 
521
                        _baseDomain);
 
522
            return 0;
 
523
        }
 
524
 
 
525
        requestedPath = localPath.path();
 
526
 
 
527
        // The domains match. Now check that the path is a sub-path of 
 
528
        // the SWF's URL. It is done by case-insensitive string comparison,
 
529
        // so a double slash in the requested path will fail.
 
530
        if (!noCaseCompare(requestedPath,
 
531
                    _basePath.substr(0, requestedPath.size()))) {
 
532
            log_security(_("SharedObject path %s is not part of the SWF path "
 
533
                        "%s. Cannot access this object."), requestedPath, 
 
534
                        _basePath);
 
535
            return 0;
 
536
        }
 
537
 
 
538
    }
 
539
 
 
540
    // A leading slash is added later
 
541
    std::ostringstream solPath;
 
542
 
 
543
    // If the domain name is empty, the SWF was loaded from the filesystem.
 
544
    // Use "localhost".
 
545
    solPath << (_baseDomain.empty() ? "localhost" : _baseDomain);
 
546
 
 
547
    // Paths should start with a '/', so we shouldn't have to add another
 
548
    // one.
 
549
    assert(requestedPath.empty() ? _basePath[0] == '/' :
 
550
                                    requestedPath[0] == '/');
 
551
 
 
552
    // If no path was requested, use the SWF's path.
 
553
    solPath << (requestedPath.empty() ? _basePath : requestedPath) << "/"
 
554
            << objName;
 
555
 
 
556
    // TODO: normalize key!
 
557
 
 
558
    const std::string& key = solPath.str();
 
559
 
 
560
    // If the shared object was already opened, use it.
 
561
    SoLib::iterator it = _soLib.find(key);
 
562
    if (it != _soLib.end()) {
 
563
        log_debug("SharedObject %s already known, returning it", key);
 
564
        return &it->second->owner();
 
565
    }
 
566
 
 
567
    log_debug("SharedObject %s not loaded. Loading it now", key);
 
568
 
 
569
    // Otherwise create a new one and register to the lib
 
570
    SharedObject_as* sh = createSharedObject(*_vm.getGlobal());
 
571
    if (!sh) return 0;
 
572
 
 
573
    sh->setObjectName(objName);
 
574
 
 
575
    std::string newspec = _solSafeDir;
 
576
    newspec += "/";
 
577
    newspec += key;
 
578
    newspec += ".sol";
 
579
    sh->setFilespec(newspec);
 
580
 
 
581
    log_debug("SharedObject path: %s", newspec);
 
582
        
 
583
    as_object* data = readSOL(_vm, newspec);
 
584
 
 
585
    /// Don't set to 0, or it will initialize a property.
 
586
    if (data) sh->setData(data);
 
587
    
 
588
    // The SharedObjectLibrary must set this as reachable.
 
589
    _soLib[key] = sh;
 
590
 
 
591
    return &sh->owner();
 
592
}
 
593
 
 
594
 
 
595
void
 
596
sharedobject_class_init(as_object& where, const ObjectURI& uri)
 
597
{
 
598
    Global_as& gl = getGlobal(where);
 
599
    as_object* proto = gl.createObject();
 
600
    attachSharedObjectInterface(*proto);
 
601
    as_object* cl = gl.createClass(&sharedobject_ctor, proto);
 
602
    attachSharedObjectStaticInterface(*cl);
 
603
    
 
604
    // Register _global.SharedObject
 
605
    where.init_member(uri, cl, as_object::DefaultFlags);    
 
606
}
 
607
 
 
608
void
 
609
registerSharedObjectNative(as_object& o)
 
610
{
 
611
    VM& vm = getVM(o);
 
612
 
 
613
    // ASnative table registration
 
614
    vm.registerNative(sharedobject_connect, 2106, 0);
 
615
    vm.registerNative(sharedobject_send, 2106, 1);
 
616
    vm.registerNative(sharedobject_flush, 2106, 2);
 
617
    vm.registerNative(sharedobject_close, 2106, 3);
 
618
    vm.registerNative(sharedobject_getSize, 2106, 4);
 
619
    vm.registerNative(sharedobject_setFps, 2106, 5);
 
620
    vm.registerNative(sharedobject_clear, 2106, 6);
 
621
 
 
622
    // FIXME: getRemote and getLocal use both these methods,
 
623
    // but aren't identical with either of them.
 
624
    // TODO: The first method looks in a library and returns either a
 
625
    // SharedObject or null. The second takes a new SharedObject as
 
626
    // its first argument and populates its data member (more or less
 
627
    // like readSOL). This is only important for ASNative compatibility.
 
628
    vm.registerNative(sharedobject_getLocal, 2106, 202);
 
629
    vm.registerNative(sharedobject_getRemote, 2106, 203);
 
630
    vm.registerNative(sharedobject_getLocal, 2106, 204);
 
631
    vm.registerNative(sharedobject_getRemote, 2106, 205);
 
632
    
 
633
    vm.registerNative(sharedobject_deleteAll, 2106, 206);
 
634
    vm.registerNative(sharedobject_getDiskUsage, 2106, 207);
 
635
}
 
636
 
 
637
 
 
638
/// SharedObject AS interface
 
639
namespace {
 
640
 
 
641
void
 
642
attachSharedObjectInterface(as_object& o)
 
643
{
 
644
 
 
645
    VM& vm = getVM(o);
 
646
 
 
647
    const int flags = PropFlags::dontEnum |
 
648
                      PropFlags::dontDelete |
 
649
                      PropFlags::onlySWF6Up;
 
650
 
 
651
    o.init_member("connect", vm.getNative(2106, 0), flags);
 
652
    o.init_member("send", vm.getNative(2106, 1), flags);
 
653
    o.init_member("flush", vm.getNative(2106, 2), flags);
 
654
    o.init_member("close", vm.getNative(2106, 3), flags);
 
655
    o.init_member("getSize", vm.getNative(2106, 4), flags);
 
656
    o.init_member("setFps", vm.getNative(2106, 5), flags);
 
657
    o.init_member("clear", vm.getNative(2106, 6), flags);
 
658
}
 
659
 
 
660
 
 
661
void
 
662
attachSharedObjectStaticInterface(as_object& o)
 
663
{
 
664
    VM& vm = getVM(o);
 
665
 
 
666
    const int flags = 0;
 
667
 
 
668
    Global_as& gl = getGlobal(o);
 
669
    o.init_member("getLocal", gl.createFunction(sharedobject_getLocal), flags);
 
670
    o.init_member("getRemote",
 
671
            gl.createFunction(sharedobject_getRemote), flags);
 
672
 
 
673
    const int hiddenOnly = PropFlags::dontEnum;
 
674
 
 
675
    o.init_member("deleteAll",  vm.getNative(2106, 206), hiddenOnly);
 
676
    o.init_member("getDiskUsage",  vm.getNative(2106, 207), hiddenOnly);
 
677
}
 
678
 
 
679
 
 
680
as_value
 
681
sharedobject_clear(const fn_call& fn)
 
682
{
 
683
    SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
 
684
    UNUSED(obj);
 
685
    
 
686
    LOG_ONCE(log_unimpl (__FUNCTION__));
 
687
 
 
688
    return as_value();
 
689
}
 
690
 
 
691
as_value
 
692
sharedobject_connect(const fn_call& fn)
 
693
{
 
694
 
 
695
    SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
 
696
    
 
697
    UNUSED(obj);
 
698
 
 
699
    if (fn.nargs < 1) {
 
700
        IF_VERBOSE_ASCODING_ERRORS(
 
701
            log_aserror(_("SharedObject.connect(): needs at least "
 
702
                    "one argument"));
 
703
        );
 
704
        return as_value();
 
705
    }
 
706
 
 
707
    LOG_ONCE(log_unimpl("SharedObject.connect()"));
 
708
 
 
709
    return as_value();
 
710
}
 
711
 
 
712
as_value
 
713
sharedobject_close(const fn_call& fn)
 
714
{
 
715
    SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
 
716
 
 
717
    obj->close();
 
718
 
 
719
    return as_value();
 
720
}
 
721
 
 
722
as_value
 
723
sharedobject_setFps(const fn_call& fn)
 
724
{
 
725
    SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
 
726
    UNUSED(obj);
 
727
 
 
728
    LOG_ONCE(log_unimpl("SharedObject.setFps"));
 
729
    return as_value();
 
730
}
 
731
 
 
732
as_value
 
733
sharedobject_send(const fn_call& fn)
 
734
{
 
735
    SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
 
736
    UNUSED(obj);
 
737
    LOG_ONCE(log_unimpl("SharedObject.send"));
 
738
    return as_value();
 
739
}
 
740
 
 
741
/// Returns false only if there was a failure writing data to file.
 
742
as_value
 
743
sharedobject_flush(const fn_call& fn)
 
744
{    
 
745
    GNASH_REPORT_FUNCTION;
 
746
 
 
747
    SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
 
748
 
 
749
    IF_VERBOSE_ASCODING_ERRORS(
 
750
        if (fn.nargs > 1)
 
751
        {
 
752
            std::ostringstream ss;
 
753
            fn.dump_args(ss);
 
754
            log_aserror(_("Arguments to SharedObject.flush(%s) will be "
 
755
                    "ignored"), ss.str());
 
756
        }
 
757
    );
 
758
 
 
759
    int space = 0;
 
760
    if (fn.nargs) {
 
761
        space = toInt(fn.arg(0));
 
762
    }
 
763
 
 
764
    /// If there is no data member, returns undefined.
 
765
    if (!obj->data()) return as_value();
 
766
 
 
767
    // If there is an object data member, returns the success of flush().
 
768
    return as_value(obj->flush(space));
 
769
}
 
770
 
 
771
// Set the file name
 
772
as_value
 
773
sharedobject_getLocal(const fn_call& fn)
 
774
{
 
775
    const int swfVersion = getSWFVersion(fn);
 
776
 
 
777
    as_value objNameVal;
 
778
    if (fn.nargs > 0) objNameVal = fn.arg(0);
 
779
 
 
780
    const std::string objName = objNameVal.to_string(swfVersion);
 
781
    if (objName.empty()) {
 
782
        IF_VERBOSE_ASCODING_ERRORS(
 
783
            std::ostringstream ss;
 
784
            fn.dump_args(ss);
 
785
            log_aserror("SharedObject.getLocal(%s): missing object name");
 
786
        );
 
787
        as_value ret;
 
788
        ret.set_null();
 
789
        return ret;
 
790
    }
 
791
 
 
792
    std::string root;
 
793
    if (fn.nargs > 1) {
 
794
        root = fn.arg(1).to_string(swfVersion);
 
795
    }
 
796
 
 
797
    log_debug("SO name:%s, root:%s", objName, root);
 
798
 
 
799
    VM& vm = getVM(fn);
 
800
 
 
801
    as_object* obj = vm.getSharedObjectLibrary().getLocal(objName, root);
 
802
 
 
803
    as_value ret(obj);
 
804
    log_debug("SharedObject.getLocal returning %s", ret);
 
805
    return ret;
 
806
}
 
807
 
 
808
as_value
 
809
sharedobject_getRemote(const fn_call& /*fn*/)
 
810
{
 
811
    LOG_ONCE(log_unimpl("SharedObject.getRemote()"));
 
812
    return as_value();
 
813
}
 
814
 
 
815
 
 
816
/// Officially undocumented.
 
817
//
 
818
/// Takes a URL argument and deletes all SharedObjects under that URL.
 
819
as_value
 
820
sharedobject_deleteAll(const fn_call& fn)
 
821
{
 
822
    SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
 
823
 
 
824
    UNUSED(obj);
 
825
 
 
826
    LOG_ONCE(log_unimpl("SharedObject.deleteAll()"));
 
827
    return as_value();
 
828
}
 
829
 
 
830
/// Undocumented
 
831
//
 
832
/// Should be quite obvious what it does.
 
833
as_value
 
834
sharedobject_getDiskUsage(const fn_call& fn)
 
835
{
 
836
 //    GNASH_REPORT_FUNCTION;
 
837
    SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
 
838
 
 
839
    UNUSED(obj);
 
840
 
 
841
    LOG_ONCE(log_unimpl("SharedObject.getDiskUsage()"));
 
842
    return as_value();
 
843
}
 
844
 
 
845
 
 
846
as_value
 
847
sharedobject_data(const fn_call& fn)
 
848
 
849
//    GNASH_REPORT_FUNCTION;
 
850
    SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
 
851
    return as_value(obj->data());
 
852
}
 
853
 
 
854
as_value
 
855
sharedobject_getSize(const fn_call& fn)
 
856
{
 
857
    SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
 
858
    return as_value(obj->size());
 
859
}
 
860
 
 
861
as_value
 
862
sharedobject_ctor(const fn_call& /*fn*/)
 
863
{
 
864
    return as_value(); 
 
865
}
 
866
 
 
867
/// Return true if the name is a valid SOL name.
 
868
//
 
869
/// The official docs claim that '%' is also an invalid character 
 
870
/// but that is incorrect (see actionscript.all/SharedObject.as)
 
871
bool
 
872
validateName(const std::string& solName)
 
873
{
 
874
    // A double forward slash isn't allowed
 
875
    std::string::size_type pos = solName.find("//");
 
876
    if (pos != std::string::npos) return false;
 
877
 
 
878
    // These character are also illegal
 
879
    pos = solName.find_first_of(",~;\"'<&>?#:\\ ");
 
880
 
 
881
    return (pos == std::string::npos);
 
882
}
 
883
 
 
884
as_object*
 
885
readSOL(VM& vm, const std::string& filespec)
 
886
{
 
887
 
 
888
    Global_as& gl = *vm.getGlobal();
 
889
 
 
890
    // The 'data' member is initialized only on getLocal() (and probably
 
891
    // getRemote()): i.e. when there is some data, or when it's ready to
 
892
    // be added.
 
893
    as_object* data = gl.createObject();
 
894
 
 
895
    struct stat st;
 
896
 
 
897
    if (stat(filespec.c_str(), &st) != 0) {
 
898
        // No existing SOL file. A new one will be created.
 
899
        log_debug("No existing SOL %s found. Will create on flush/exit.",
 
900
                  filespec);
 
901
        return data;
 
902
    }
 
903
 
 
904
    const size_t size = st.st_size;
 
905
 
 
906
    if (size < 28) {
 
907
        // A SOL file exists, but it was invalid. Count it as not existing.
 
908
        log_error("readSOL: SOL file %s is too short "
 
909
                  "(only %s bytes long) to be valid.", filespec, st.st_size);
 
910
        return data;
 
911
    }
 
912
 
 
913
    boost::scoped_array<boost::uint8_t> sbuf(new boost::uint8_t[size]);
 
914
    const boost::uint8_t *buf = sbuf.get();
 
915
    const boost::uint8_t *end = buf + size;
 
916
 
 
917
    try {
 
918
        std::ifstream ifs(filespec.c_str(), std::ios::binary);
 
919
        ifs.read(reinterpret_cast<char*>(sbuf.get()), size);
 
920
 
 
921
        // TODO check initial bytes, and print warnings if they are fishy
 
922
 
 
923
        buf += 16; // skip const-length headers
 
924
 
 
925
        // skip past name   TODO add sanity check
 
926
        buf += ntohs(*(reinterpret_cast<const boost::uint16_t*>(buf)));
 
927
        buf += 2;
 
928
        
 
929
        buf += 4; // skip past padding
 
930
 
 
931
        if (buf >= end) {
 
932
            // In this case there is no data member.
 
933
            log_error("readSOL: file ends before data segment");
 
934
            return data;
 
935
        }
 
936
 
 
937
        amf::Reader rd(buf, end, gl);
 
938
 
 
939
        while (buf != end) {
 
940
 
 
941
            log_debug("readSOL: reading property name at "
 
942
                    "byte %s", buf - sbuf.get());
 
943
            // read property name
 
944
            
 
945
            if (end - buf < 2) {
 
946
                log_error("SharedObject: end of buffer while reading length");
 
947
                break;
 
948
            }
 
949
 
 
950
            const boost::uint16_t len = 
 
951
                ntohs(*(reinterpret_cast<const boost::uint16_t*>(buf)));
 
952
            buf += 2;
 
953
 
 
954
            if (!len) {
 
955
                log_error("readSOL: empty property name");
 
956
                break;
 
957
            }
 
958
 
 
959
            if (end - buf < len) {
 
960
                log_error("SharedObject::readSOL: premature end of input");
 
961
                break;
 
962
            }
 
963
 
 
964
            std::string prop_name(reinterpret_cast<const char*>(buf), len);
 
965
            buf += len;
 
966
 
 
967
            // read value
 
968
            as_value as;
 
969
 
 
970
            if (!rd(as)) {
 
971
                log_error("SharedObject: error parsing SharedObject '%s'",
 
972
                        filespec);
 
973
                return 0;
 
974
            }
 
975
 
 
976
            log_debug("parsed sol member named '%s' (len %s),  value '%s'",
 
977
                    prop_name, len, as);
 
978
 
 
979
            // set name/value as a member of this (SharedObject) object
 
980
            string_table& st = vm.getStringTable();
 
981
            data->set_member(st.find(prop_name), as);
 
982
            
 
983
            if (buf == end) break;;
 
984
 
 
985
            buf += 1; // skip null byte after each property
 
986
        }
 
987
        return data;
 
988
    }
 
989
 
 
990
    catch (std::exception& e) {
 
991
        log_error("readSOL: Reading SharedObject %s: %s", 
 
992
                  filespec, e.what());
 
993
        return 0;
 
994
    }
 
995
 
 
996
}
 
997
 
 
998
 
 
999
void
 
1000
flushSOL(SharedObjectLibrary::SoLib::value_type& sol)
 
1001
{
 
1002
    sol.second->flush();
 
1003
}
 
1004
 
 
1005
SharedObject_as*
 
1006
createSharedObject(Global_as& gl)
 
1007
{
 
1008
    as_function* ctor = gl.getMember(NSV::CLASS_SHARED_OBJECT).to_function();
 
1009
    if (!ctor) return 0;
 
1010
    as_environment env(getVM(gl));
 
1011
    fn_call::Args args;
 
1012
    as_object* o = constructInstance(*ctor, env, args);
 
1013
 
 
1014
    std::auto_ptr<SharedObject_as> sh(new SharedObject_as(*o));
 
1015
    o->setRelay(sh.release());
 
1016
 
 
1017
    // We know what it is...
 
1018
    return &static_cast<SharedObject_as&>(*o->relay());;
 
1019
}
 
1020
 
 
1021
/// Encode header data.
 
1022
//
 
1023
/// Note that the separation of header and data here is arbitrary.
 
1024
void
 
1025
encodeHeader(const size_t size, SimpleBuffer& buf)
 
1026
{
 
1027
    const boost::uint8_t header[] = { 0x00, 0xbf };
 
1028
    
 
1029
    // Initial header byters
 
1030
    buf.append(header, arraySize(header));
 
1031
    
 
1032
    // Size of data plus 14 (complete size of data after size field).
 
1033
    buf.appendNetworkLong(size);
 
1034
}
 
1035
 
 
1036
/// This writes everything after the 'length' field of the SOL data.
 
1037
bool
 
1038
encodeData(const std::string& name, as_object& data, SimpleBuffer& buf)
 
1039
{
 
1040
    // Write the remaining header-like information.
 
1041
    const boost::uint8_t magic[] = { 'T', 'C', 'S', 'O',
 
1042
        0x00, 0x04, 0x00, 0x00, 0x00, 0x00 };
 
1043
 
 
1044
    // Magic SharedObject bytes.
 
1045
    buf.append(magic, arraySize(magic)); 
 
1046
 
 
1047
    // SharedObject name
 
1048
    const boost::uint16_t len = name.length();
 
1049
    buf.appendNetworkShort(len);
 
1050
    buf.append(name.c_str(), len);
 
1051
 
 
1052
    // Padding
 
1053
    const boost::uint8_t padding[] = { 0, 0, 0, 0 };
 
1054
    buf.append(padding, arraySize(padding));
 
1055
    
 
1056
    // see http://osflash.org/documentation/amf/envelopes/sharedobject
 
1057
    // Do not encode strict arrays!
 
1058
    amf::Writer w(buf, false);
 
1059
    string_table& st = getStringTable(data);
 
1060
 
 
1061
    SOLPropsBufSerializer props(w, st);
 
1062
 
 
1063
    // Visit all existing properties.
 
1064
    data.visitProperties<Exists>(props);
 
1065
 
 
1066
    if (!props.success()) {
 
1067
        // There are good reasons for this to fail, so it's not an error. Real
 
1068
        // errors are logged during serialization.
 
1069
        log_debug("Did not serialize object");
 
1070
        return false;
 
1071
    }
 
1072
    return true;
 
1073
}
 
1074
 
 
1075
} // anonymous namespace
 
1076
} // end of gnash namespace