2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
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.
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.
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
19 #include "GnashSystemNetHeaders.h"
20 #include "GnashFileUtilities.h"
26 #include "GnashException.h"
28 #include <boost/scoped_array.hpp>
29 #include <boost/cstdint.hpp>
30 #include <boost/shared_ptr.hpp>
39 using gnash::ParserException
;
40 using gnash::log_debug
;
41 using gnash::log_error
;
43 // It comprises of a magic number, followed by the file length, a
44 // filetype, which appears to always be "TCSO", and what appears to be
45 // a marker at the end of the header block.
46 // After the SOL header, the rest is all AMF objects.
48 // Magic Number - 2 bytes (always 0x00bf)
49 // Length - 4 bytes (the length of the file including the Marker bytes)
50 // Marker - 10 bytes (always "TCSO0x000400000000")
51 // Object Name - variable (the name of the object as an AMF encoded string)
53 // After this is a series of AMF objects
54 const short SOL_MAGIC
= 0x00bf; // is in big-endian format, this is the first
55 // two bytes. of the .sol file.
56 //char *SOL_FILETYPE = "TCSO";
57 const short SOL_BLOCK_MARK
= 0x0004;
59 /// \define ENSUREBYTES
61 /// @param from The base address to check.
63 /// @param tooFar The ending address that is one byte too many.
65 /// @param size The number of bytes to check for: from to tooFar.
67 /// @remarks May throw an Exception
68 #define ENSUREBYTES(from, toofar, size) { \
69 if ( from+size >= toofar ) \
70 throw ParserException("Premature end of AMF stream"); \
75 /// This namespace is for all the AMF specific classes in libamf.
82 // GNASH_REPORT_FUNCTION;
87 // GNASH_REPORT_FUNCTION;
90 /// \brief Extract the header from the file.
92 /// @param filespec The name and path of the .sol file to parse.
94 /// @return true if this succeeded. false if it doesn't.
96 SOL::extractHeader(const std::string
& /*filespec*/)
98 // GNASH_REPORT_FUNCTION;
102 /// \brief Extract the header from the file.
104 /// @param data a reference to a vector of bytes that contains the
107 /// @return true if this succeeded. false if it doesn't.
109 SOL::extractHeader(const vector
<unsigned char> & /*data*/)
111 // GNASH_REPORT_FUNCTION;
115 /// \brief Add the AMF objects that are the data of the file
117 /// @param el A smart pointer to the Element to add to the .sol file.
121 SOL::addObj(boost::shared_ptr
<cygnal::Element
> el
)
123 // GNASH_REPORT_FUNCTION;
124 _amfobjs
.push_back(el
);
125 // _filesize += el->getName().size() + el->getDataSize() + 5;
128 /// \brief Create the file header.
130 /// @param data a reference to a vector of bytes that contains the
133 /// @return true if this succeeded. false if it doesn't.
135 SOL::formatHeader(const vector
<unsigned char> & /*data*/)
137 // GNASH_REPORT_FUNCTION;
141 /// \brief Create the file header.
143 /// @param name The name of the SharedObject for this file.
145 /// @return true if this succeeded. false if it doesn't.
147 SOL::formatHeader(const std::string
&name
)
149 return formatHeader(name
, _filesize
);
152 /// \brief Create the file header.
154 /// @param name The name of the SharedObject for this file.
156 /// @param filesize The size of the file.
158 /// @return true if this succeeded. false if it doesn't.
160 SOL::formatHeader(const std::string
&name
, int filesize
)
162 // GNASH_REPORT_FUNCTION;
165 // First we add the magic number. All SOL data is in big-endian format,
166 // so we swap it first.
167 boost::uint16_t swapped
= SOL_MAGIC
;
168 swapped
= htons(swapped
);
169 boost::uint8_t *ptr
= reinterpret_cast<boost::uint8_t *>(&swapped
);
170 for (i
=0; i
<sizeof(boost::uint16_t); i
++) {
171 _header
.push_back(ptr
[i
]);
174 // Next is the file size to be created. We adjust it as the filesize
175 // includes the padding in the header, the mystery bytes, and the
176 // padding, plus the length of the name itself.
177 filesize
+= name
.size() + 16;
178 boost::uint32_t len
= filesize
;
180 ptr
= reinterpret_cast<boost::uint8_t *>(&len
);
181 for (i
=0; i
<sizeof(boost::uint32_t); i
++) {
182 _header
.push_back(ptr
[i
]);
185 // Then the mystery block, but as the value never seems to every change,
186 // we just built it the same way it always is.
187 // first is the TCSO, we have no idea what this stands for.
188 // ptr = reinterpret_cast<uint8_t *>(const_cast<uint8_t *>("TCSO");
189 ptr
= (boost::uint8_t *)"TCSO";
190 for (i
=0; i
<sizeof(boost::uint32_t); i
++) {
191 _header
.push_back(ptr
[i
]);
193 // then the 0x0004 bytes, also a mystery
194 swapped
= SOL_BLOCK_MARK
;
195 swapped
= htons(swapped
);
196 ptr
= reinterpret_cast<boost::uint8_t *>(&swapped
);
197 for (i
=0; i
<sizeof(boost::uint16_t); i
++) {
198 _header
.push_back(ptr
[i
]);
200 // finally a bunch of zeros to pad things for this field
201 for (i
=0; i
<sizeof(boost::uint32_t); i
++) {
202 _header
.push_back('\0');
205 // Encode the name. This is not a string object, which has a type field
206 // one byte field precedding the length as a file type of AMF::STRING.
207 // First the length in two bytes
208 swapped
= name
.size();
209 swapped
= htons(swapped
);
210 ptr
= reinterpret_cast<boost::uint8_t *>(&swapped
);
211 for (i
=0; i
<sizeof(boost::uint16_t); i
++) {
212 _header
.push_back(ptr
[i
]);
214 // then the string itself
215 ptr
= (boost::uint8_t *)name
.c_str();
216 for (i
=0; i
<name
.size(); i
++) {
217 _header
.push_back(ptr
[i
]);
220 // finally a bunch of zeros to pad things at the end of the header
221 for (i
=0; i
<sizeof(boost::uint32_t); i
++) {
222 _header
.push_back('\0');
226 unsigned char *hexint
;
227 hexint
= new unsigned char[(_header
.size() + 3) *3];
229 hexify(hexint
, (unsigned char *)_header
, _header
.size(), true);
230 log_debug (_("%s: SOL file header is: \n%s"), __FUNCTION__
, (char *)hexint
);
237 /// \brief Write the data to disk as a .sol file
239 /// @param filespec The name and path of the .sol file to parse.
241 /// @param name The name of the SharedObject for this file.
243 /// @return true if this succeeded. false if it doesn't.
245 SOL::writeFile(const std::string
&filespec
, const std::string
&name
)
247 // GNASH_REPORT_FUNCTION;
248 std::ofstream
ofs(filespec
.c_str(), std::ios::binary
);
250 log_error("Failed opening file '%s' in binary mode", filespec
.c_str());
254 vector
<boost::uint8_t>::iterator it
;
255 vector
<boost::shared_ptr
<cygnal::Element
> >::iterator ita
;
260 if (filespec
.empty()) {
264 for (ita
= _amfobjs
.begin(); ita
!= _amfobjs
.end(); ita
++) {
265 boost::shared_ptr
<cygnal::Element
> el
= (*(ita
));
266 size
+= el
->getNameSize() + el
->getDataSize() + 7;
270 boost::scoped_array
<char> body ( new char[size
+ 20] );
271 std::fill_n(body
.get(), size
, 0);
273 char* endPtr
= ptr
+size
+20; // that's the amount we allocated..
275 for (ita
= _amfobjs
.begin(); ita
!= _amfobjs
.end(); ita
++) {
276 boost::shared_ptr
<Element
> el
= (*(ita
));
277 boost::shared_ptr
<cygnal::Buffer
> var
= amf_obj
.encodeProperty(el
);
278 // boost::uint8_t *var = amf_obj.encodeProperty(el, outsize);
283 switch (el
->getType()) {
284 case Element::BOOLEAN_AMF0
:
285 outsize
= el
->getNameSize() + 4;
286 memcpy(ptr
, var
->reference(), outsize
);
289 case Element::OBJECT_AMF0
:
290 outsize
= el
->getNameSize() + 5;
291 assert(ptr
+outsize
< endPtr
);
292 // outsize = el->getNameSize() + 5;
293 memcpy(ptr
, var
->reference(), outsize
);
295 *ptr
++ = Element::OBJECT_END_AMF0
;
296 // *ptr++ = 0; // objects are terminated too!
298 case Element::NUMBER_AMF0
:
299 outsize
= el
->getNameSize() + AMF0_NUMBER_SIZE
+ 3;
300 assert(ptr
+outsize
< endPtr
);
301 memcpy(ptr
, var
->reference(), outsize
);
303 // *ptr++ = 0; // doubles are terminated too!
304 // *ptr++ = 0; // doubles are terminated too!
306 case Element::STRING_AMF0
:
307 if (el
->getDataSize() == 0) {
308 assert(ptr
+outsize
+1 < endPtr
);
309 memcpy(ptr
, var
->reference(), outsize
+1);
311 } else { // null terminate the string
312 assert(ptr
+outsize
< endPtr
);
313 memcpy(ptr
, var
->reference(), outsize
);
319 assert(ptr
+outsize
< endPtr
);
320 memcpy(ptr
, var
->reference(), outsize
);
325 _filesize
= ptr
- body
.get();
326 int len
= name
.size() + sizeof(boost::uint16_t) + 16;
327 boost::scoped_array
<char> head ( new char[len
+ 4] );
328 memset(head
.get(), 0, len
);
331 for (it
= _header
.begin(); it
!= _header
.end(); it
++) {
335 if ( ofs
.write(head
.get(), _header
.size()).fail() )
337 log_error("Error writing %d bytes of header to output file %s",
338 _header
.size(), filespec
);
342 if ( ofs
.write(body
.get(), _filesize
).fail() )
344 log_error("Error writing %d bytes of body to output file %s",
345 _filesize
, filespec
);
353 /// \brief Read a .sol file from disk
355 /// @param filespec The name and path of the .sol file to parse.
357 /// @return true if this succeeded. false if it doesn't.
359 SOL::readFile(const std::string
&filespec
)
361 // GNASH_REPORT_FUNCTION;
363 boost::uint16_t size
;
366 // Make sure it's an SOL file
367 if (stat(filespec
.c_str(), &st
) != 0) return false;
371 boost::uint8_t *ptr
= 0;
373 std::ifstream
ifs(filespec
.c_str(), std::ios::binary
);
375 _filesize
= st
.st_size
;
376 boost::scoped_array
<boost::uint8_t> buf(
377 new boost::uint8_t[_filesize
+ sizeof(int)]);
380 boost::uint8_t* tooFar
= buf
.get() + _filesize
;
382 bodysize
= st
.st_size
- 6;
383 _filespec
= filespec
;
384 ifs
.read(reinterpret_cast<char *>(ptr
), _filesize
);
386 #ifndef GNASH_TRUST_AMF
387 ENSUREBYTES(ptr
, tooFar
, 2+4+10); // magic number, file size, file marker
390 // skip the magic number (will check later)
393 // extract the file size
394 boost::uint32_t length
= *(reinterpret_cast<boost::uint32_t *>(ptr
));
395 length
= ntohl(length
);
398 // skip the file marker field
402 if ((buf
[0] == 0) && (buf
[1] == 0xbf)) {
403 if (bodysize
== length
) {
404 log_debug("%s is an SOL file", filespec
);
407 log_error("%s looks like an SOL file, but the length is wrong. "
408 "Should be %d, got %d",
409 filespec
, (_filesize
- 6), length
);
413 log_error("%s isn't an SOL file", filespec
);
416 #ifndef GNASH_TRUST_AMF
417 ENSUREBYTES(ptr
, tooFar
, 2);
420 // 2 bytes for the length of the object name, but it's also
422 size
= *(reinterpret_cast<boost::uint16_t *>(ptr
));
426 #ifndef GNASH_TRUST_AMF
427 ENSUREBYTES(ptr
, tooFar
, size
+4); // 4 is the padding below
430 // TODO: make sure there's the null-termination, or
431 // force if if it's there by definition
432 _objname
= reinterpret_cast<const char *>(ptr
);
435 // Go past the padding
439 boost::shared_ptr
<cygnal::Element
> el
;
440 while ( ptr
< tooFar
) {
442 el
= amf_obj
.extractProperty(ptr
, tooFar
);
444 ptr
+= amf_obj
.totalsize() + 1;
445 _amfobjs
.push_back(el
);
454 catch (std::exception
& e
) {
455 log_error("Reading SharedObject %s: %s", filespec
, e
.what());
462 SOL::updateSO(boost::shared_ptr
<cygnal::Element
> &newel
)
464 // GNASH_REPORT_FUNCTION;
465 vector
<boost::shared_ptr
<cygnal::Element
> >::iterator ita
;
466 for (ita
= _amfobjs
.begin(); ita
!= _amfobjs
.end(); ita
++) {
467 boost::shared_ptr
<cygnal::Element
> oldel
= (*(ita
));
468 if (oldel
== newel
) {
476 SOL::updateSO(int index
, boost::shared_ptr
<cygnal::Element
> &el
)
478 // GNASH_REPORT_FUNCTION;
479 _amfobjs
[index
] = el
;
483 /// \brief Dump the internal data of this class in a human readable form.
485 /// @remarks This should only be used for debugging purposes.
490 vector
<boost::shared_ptr
<cygnal::Element
> >::iterator it
;
492 cerr
<< "Dumping SOL file" << endl
;
493 cerr
<< "The file name is: " << _filespec
<< endl
;
494 cerr
<< "The size of the file is: " << _filesize
<< endl
;
495 cerr
<< "The name of the object is: " << _objname
<< endl
;
496 for (it
= _amfobjs
.begin(); it
!= _amfobjs
.end(); it
++) {
497 boost::shared_ptr
<cygnal::Element
> el
= (*(it
));
498 cerr
<< el
->getName() << ": ";
499 if (el
->getType() == Element::STRING_AMF0
) {
500 if (el
->getDataSize() != 0) {
501 cerr
<< el
->to_string();
506 if (el
->getType() == Element::NUMBER_AMF0
) {
507 double ddd
= el
->to_number();
508 swapBytes(&ddd
, sizeof(double));
510 // cerr << "( " << hexify(el->to_(), 8, false) << ")";
512 if (el
->getType() == Element::BOOLEAN_AMF0
) {
513 if (el
->to_bool() == true) {
516 if (el
->to_bool() == false) {
520 if (el
->getType() == Element::OBJECT_AMF0
) {
521 cerr
<< "is an object";
528 } // end of amf namespace
532 // indent-tabs-mode: t