1
// SharedObject_as.cpp: ActionScript "SharedObject" class, for Gnash.
3
// Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
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
22
#include "gnashconfig.h"
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"
31
#include "SharedObject_as.h"
32
#include "as_object.h" // for inheritance
35
#include "Global_as.h"
36
#include "builtin_function.h" // need builtin_function
37
#include "NativeFunction.h"
40
#include "string_table.h"
41
#include "rc.h" // for use of rcfile
42
#include "URLAccessManager.h"
44
#include "NetConnection_as.h"
46
#include "AMFConverter.h"
47
#include "GnashAlgorithm.h"
48
#include "RunResources.h"
49
#include "namedStrings.h"
51
#include <boost/scoped_array.hpp>
52
#include <boost/shared_ptr.hpp>
56
gnash::RcInitFile& rcfile = gnash::RcInitFile::getDefaultInstance();
62
// Forward declarations
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);
79
as_object* readSOL(VM& vm, const std::string& filespec);
81
/// Encode the SharedObject data.
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,
89
/// Encode the 2 header bytes and data length field.
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);
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);
100
SharedObject_as* createSharedObject(Global_as& gl);
106
/// Class used to serialize properties of an object to a buffer in SOL format
107
class SOLPropsBufSerializer : public AbstractPropertyVisitor
112
SOLPropsBufSerializer(amf::Writer w, string_table& st)
120
/// Check if the object data was successfully encoded.
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;
128
/// Visitor called for each property to serialize.
130
/// SOL serialization calls this for all existing properties, whether
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)
139
if (val.is_function()) {
140
log_debug("SOL: skip serialization of FUNCTION property");
144
const string_table::key key = getName(uri);
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) {
156
// write property name
157
const std::string& name = _st.value(key);
159
_writer.writePropertyName(name);
161
// Strict array are never encoded in SharedObject
162
if (!val.writeAMF0(_writer)) {
163
log_error("Problems serializing an object's member %s=%s",
171
// This is SOL specific.
172
boost::uint8_t end(0);
173
_writer.writeData(&end, 1);
182
/// String table for looking up property names as strings.
185
/// Whether an error has been encountered.
188
/// How many properties have been encoded.
190
/// A boolean would do just as nicely, but we may want to use the count
195
} // anonymous namespace
197
class SharedObject_as : public Relay
201
SharedObject_as(as_object& owner)
209
virtual ~SharedObject_as();
211
/// The as_object that owns this Relay.
216
/// Write the data as a SOL file.
218
/// If there is no data to write, the file is removed.
219
bool flush(int space = 0) const;
221
/// The filename of this SharedObject.
222
const std::string& getFilespec() const {
226
/// Set the filename of the SharedObject.
227
void setFilespec(const std::string& s) {
231
/// Set the name of this SharedObject.
232
void setObjectName(const std::string& s) {
236
/// Return the size of the data plus header.
237
size_t size() const {
238
if (!_data) return 0;
241
// The header comprises 2 bytes and a length field of 4 bytes.
242
if (encodeData(_name, *_data, buf)) {
243
return buf.size() + 6;
248
/// Set the data object.
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) {
261
const int flags = PropFlags::dontDelete |
264
_owner.init_property(NSV::PROP_DATA, &sharedobject_data,
265
&sharedobject_data, flags);
269
/// Get the data object.
270
as_object* data() const {
274
/// Close the SharedObject
276
/// Note that this currently does nothing.
279
/// Are we connected?
280
bool connected() const { return _connected; }
282
/// Override from Relay.
283
virtual void setReachable();
287
/// The as_object to which this Relay belongs.
290
/// An object to store and access the actual data.
295
std::string _filename;
297
/// Are we connected? (No).
303
SharedObject_as::~SharedObject_as()
308
/// Returns false if the data cannot be written to file.
310
/// If there is no data, the file is removed and the function returns true.
312
SharedObject_as::flush(int space) const
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.
318
/// The function should never be called from SharedObject.flush() when
320
if (!_data) return false;
323
log_unimpl("SharedObject.flush() called with a minimum disk space "
324
"argument (%d), which is currently ignored", space);
327
const std::string& filespec = getFilespec();
329
if (!mkdirRecursive(filespec)) {
330
log_error("Couldn't create dir for flushing SharedObject %s", filespec);
334
#ifdef USE_SOL_READONLY
335
log_debug(_("SharedObject %s not flushed (compiled as read-only mode)"),
340
if (rcfile.getSOLReadOnly()) {
341
log_security("Attempting to write object %s when it's SOL "
342
"Read Only is set! Refusing...", filespec);
347
std::ofstream ofs(filespec.c_str(), std::ios::binary);
349
log_error("SharedObject::flush(): Failed opening file '%s' in "
350
"binary mode", filespec.c_str());
356
if (!encodeData(_name, *_data, buf)) {
357
std::remove(filespec.c_str());
361
// Encode header part.
363
encodeHeader(buf.size(), header);
366
ofs.write(reinterpret_cast<const char*>(header.data()), header.size());
368
log_error("Error writing SOL header");
373
ofs.write(reinterpret_cast<const char*>(buf.data()), buf.size());
375
log_error("Error writing %d bytes to output file %s",
376
buf.size(), filespec.c_str());
381
log_security("SharedObject '%s' written to filesystem.", filespec);
386
SharedObject_as::close()
390
SharedObjectLibrary::SharedObjectLibrary(VM& vm)
395
_solSafeDir = rcfile.getSOLSafeDir();
396
if (_solSafeDir.empty()) {
397
log_debug("Empty SOLSafeDir directive: we'll use '/tmp'");
398
_solSafeDir = "/tmp/";
401
// Check if the base dir exists here
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));
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.
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).
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.
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
425
const movie_root& mr = _vm.getRoot();
426
const std::string& swfURL = mr.getOriginalURL();
430
// Remember the hostname of our SWF URL. This can be empty if loaded
431
// from the filesystem
432
_baseDomain = url.hostname();
434
const std::string& urlPath = url.path();
436
// Get the path part. If loaded from the filesystem, the pp stupidly
437
// removes the first directory.
438
if (!_baseDomain.empty()) {
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);
452
SharedObject_as::setReachable()
454
_owner.setReachable();
455
if (_data) _data->setReachable();
459
SharedObjectLibrary::markReachableResources() const
461
foreachSecond(_soLib.begin(), _soLib.end(),
462
&SharedObject_as::setReachable);
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
470
SharedObjectLibrary::clear()
472
std::for_each(_soLib.begin(), _soLib.end(), &flushSOL);
476
SharedObjectLibrary::~SharedObjectLibrary()
482
SharedObjectLibrary::getLocal(const std::string& objName,
483
const std::string& root)
485
assert (!objName.empty());
487
// already warned about it at construction time
488
if (_solSafeDir.empty()) return 0;
490
if (rcfile.getSOLLocalDomain() && !_baseDomain.empty())
492
log_security("Attempting to open SOL file from non "
493
"localhost-loaded SWF");
497
// Check that the name is valid; if not, return null
498
if (!validateName(objName)) return 0;
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
503
std::string requestedPath;
505
// If a root is specified, check it first for validity
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);
514
StringNoCaseEqual noCaseCompare;
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,
525
requestedPath = localPath.path();
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,
540
// A leading slash is added later
541
std::ostringstream solPath;
543
// If the domain name is empty, the SWF was loaded from the filesystem.
545
solPath << (_baseDomain.empty() ? "localhost" : _baseDomain);
547
// Paths should start with a '/', so we shouldn't have to add another
549
assert(requestedPath.empty() ? _basePath[0] == '/' :
550
requestedPath[0] == '/');
552
// If no path was requested, use the SWF's path.
553
solPath << (requestedPath.empty() ? _basePath : requestedPath) << "/"
556
// TODO: normalize key!
558
const std::string& key = solPath.str();
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();
567
log_debug("SharedObject %s not loaded. Loading it now", key);
569
// Otherwise create a new one and register to the lib
570
SharedObject_as* sh = createSharedObject(*_vm.getGlobal());
573
sh->setObjectName(objName);
575
std::string newspec = _solSafeDir;
579
sh->setFilespec(newspec);
581
log_debug("SharedObject path: %s", newspec);
583
as_object* data = readSOL(_vm, newspec);
585
/// Don't set to 0, or it will initialize a property.
586
if (data) sh->setData(data);
588
// The SharedObjectLibrary must set this as reachable.
596
sharedobject_class_init(as_object& where, const ObjectURI& uri)
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);
604
// Register _global.SharedObject
605
where.init_member(uri, cl, as_object::DefaultFlags);
609
registerSharedObjectNative(as_object& o)
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);
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);
633
vm.registerNative(sharedobject_deleteAll, 2106, 206);
634
vm.registerNative(sharedobject_getDiskUsage, 2106, 207);
638
/// SharedObject AS interface
642
attachSharedObjectInterface(as_object& o)
647
const int flags = PropFlags::dontEnum |
648
PropFlags::dontDelete |
649
PropFlags::onlySWF6Up;
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);
662
attachSharedObjectStaticInterface(as_object& o)
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);
673
const int hiddenOnly = PropFlags::dontEnum;
675
o.init_member("deleteAll", vm.getNative(2106, 206), hiddenOnly);
676
o.init_member("getDiskUsage", vm.getNative(2106, 207), hiddenOnly);
681
sharedobject_clear(const fn_call& fn)
683
SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
686
LOG_ONCE(log_unimpl (__FUNCTION__));
692
sharedobject_connect(const fn_call& fn)
695
SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
700
IF_VERBOSE_ASCODING_ERRORS(
701
log_aserror(_("SharedObject.connect(): needs at least "
707
LOG_ONCE(log_unimpl("SharedObject.connect()"));
713
sharedobject_close(const fn_call& fn)
715
SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
723
sharedobject_setFps(const fn_call& fn)
725
SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
728
LOG_ONCE(log_unimpl("SharedObject.setFps"));
733
sharedobject_send(const fn_call& fn)
735
SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
737
LOG_ONCE(log_unimpl("SharedObject.send"));
741
/// Returns false only if there was a failure writing data to file.
743
sharedobject_flush(const fn_call& fn)
745
GNASH_REPORT_FUNCTION;
747
SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
749
IF_VERBOSE_ASCODING_ERRORS(
752
std::ostringstream ss;
754
log_aserror(_("Arguments to SharedObject.flush(%s) will be "
755
"ignored"), ss.str());
761
space = toInt(fn.arg(0));
764
/// If there is no data member, returns undefined.
765
if (!obj->data()) return as_value();
767
// If there is an object data member, returns the success of flush().
768
return as_value(obj->flush(space));
773
sharedobject_getLocal(const fn_call& fn)
775
const int swfVersion = getSWFVersion(fn);
778
if (fn.nargs > 0) objNameVal = fn.arg(0);
780
const std::string objName = objNameVal.to_string(swfVersion);
781
if (objName.empty()) {
782
IF_VERBOSE_ASCODING_ERRORS(
783
std::ostringstream ss;
785
log_aserror("SharedObject.getLocal(%s): missing object name");
794
root = fn.arg(1).to_string(swfVersion);
797
log_debug("SO name:%s, root:%s", objName, root);
801
as_object* obj = vm.getSharedObjectLibrary().getLocal(objName, root);
804
log_debug("SharedObject.getLocal returning %s", ret);
809
sharedobject_getRemote(const fn_call& /*fn*/)
811
LOG_ONCE(log_unimpl("SharedObject.getRemote()"));
816
/// Officially undocumented.
818
/// Takes a URL argument and deletes all SharedObjects under that URL.
820
sharedobject_deleteAll(const fn_call& fn)
822
SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
826
LOG_ONCE(log_unimpl("SharedObject.deleteAll()"));
832
/// Should be quite obvious what it does.
834
sharedobject_getDiskUsage(const fn_call& fn)
836
// GNASH_REPORT_FUNCTION;
837
SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
841
LOG_ONCE(log_unimpl("SharedObject.getDiskUsage()"));
847
sharedobject_data(const fn_call& fn)
849
// GNASH_REPORT_FUNCTION;
850
SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
851
return as_value(obj->data());
855
sharedobject_getSize(const fn_call& fn)
857
SharedObject_as* obj = ensure<ThisIsNative<SharedObject_as> >(fn);
858
return as_value(obj->size());
862
sharedobject_ctor(const fn_call& /*fn*/)
867
/// Return true if the name is a valid SOL name.
869
/// The official docs claim that '%' is also an invalid character
870
/// but that is incorrect (see actionscript.all/SharedObject.as)
872
validateName(const std::string& solName)
874
// A double forward slash isn't allowed
875
std::string::size_type pos = solName.find("//");
876
if (pos != std::string::npos) return false;
878
// These character are also illegal
879
pos = solName.find_first_of(",~;\"'<&>?#:\\ ");
881
return (pos == std::string::npos);
885
readSOL(VM& vm, const std::string& filespec)
888
Global_as& gl = *vm.getGlobal();
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
893
as_object* data = gl.createObject();
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.",
904
const size_t size = st.st_size;
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);
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;
918
std::ifstream ifs(filespec.c_str(), std::ios::binary);
919
ifs.read(reinterpret_cast<char*>(sbuf.get()), size);
921
// TODO check initial bytes, and print warnings if they are fishy
923
buf += 16; // skip const-length headers
925
// skip past name TODO add sanity check
926
buf += ntohs(*(reinterpret_cast<const boost::uint16_t*>(buf)));
929
buf += 4; // skip past padding
932
// In this case there is no data member.
933
log_error("readSOL: file ends before data segment");
937
amf::Reader rd(buf, end, gl);
941
log_debug("readSOL: reading property name at "
942
"byte %s", buf - sbuf.get());
943
// read property name
946
log_error("SharedObject: end of buffer while reading length");
950
const boost::uint16_t len =
951
ntohs(*(reinterpret_cast<const boost::uint16_t*>(buf)));
955
log_error("readSOL: empty property name");
959
if (end - buf < len) {
960
log_error("SharedObject::readSOL: premature end of input");
964
std::string prop_name(reinterpret_cast<const char*>(buf), len);
971
log_error("SharedObject: error parsing SharedObject '%s'",
976
log_debug("parsed sol member named '%s' (len %s), value '%s'",
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);
983
if (buf == end) break;;
985
buf += 1; // skip null byte after each property
990
catch (std::exception& e) {
991
log_error("readSOL: Reading SharedObject %s: %s",
1000
flushSOL(SharedObjectLibrary::SoLib::value_type& sol)
1002
sol.second->flush();
1006
createSharedObject(Global_as& gl)
1008
as_function* ctor = gl.getMember(NSV::CLASS_SHARED_OBJECT).to_function();
1009
if (!ctor) return 0;
1010
as_environment env(getVM(gl));
1012
as_object* o = constructInstance(*ctor, env, args);
1014
std::auto_ptr<SharedObject_as> sh(new SharedObject_as(*o));
1015
o->setRelay(sh.release());
1017
// We know what it is...
1018
return &static_cast<SharedObject_as&>(*o->relay());;
1021
/// Encode header data.
1023
/// Note that the separation of header and data here is arbitrary.
1025
encodeHeader(const size_t size, SimpleBuffer& buf)
1027
const boost::uint8_t header[] = { 0x00, 0xbf };
1029
// Initial header byters
1030
buf.append(header, arraySize(header));
1032
// Size of data plus 14 (complete size of data after size field).
1033
buf.appendNetworkLong(size);
1036
/// This writes everything after the 'length' field of the SOL data.
1038
encodeData(const std::string& name, as_object& data, SimpleBuffer& buf)
1040
// Write the remaining header-like information.
1041
const boost::uint8_t magic[] = { 'T', 'C', 'S', 'O',
1042
0x00, 0x04, 0x00, 0x00, 0x00, 0x00 };
1044
// Magic SharedObject bytes.
1045
buf.append(magic, arraySize(magic));
1047
// SharedObject name
1048
const boost::uint16_t len = name.length();
1049
buf.appendNetworkShort(len);
1050
buf.append(name.c_str(), len);
1053
const boost::uint8_t padding[] = { 0, 0, 0, 0 };
1054
buf.append(padding, arraySize(padding));
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);
1061
SOLPropsBufSerializer props(w, st);
1063
// Visit all existing properties.
1064
data.visitProperties<Exists>(props);
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");
1075
} // anonymous namespace
1076
} // end of gnash namespace