1 //
2 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
3 //   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 #ifdef HAVE_CONFIG_H
20 #include "gnashconfig.h"
21 #endif
22 
23 //#ifdef HAVE_DEJAGNU_H
24 #if 1
25 #include <sys/types.h>
26 extern "C"{
27 #include "GnashSystemIOHeaders.h"
28 #ifdef HAVE_GETOPT_H
29 #include <getopt.h>
30 #endif
31 #ifndef __GNUC__
32 extern int optind, getopt(int, char *const *, const char *);
33 #endif
34 }
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <string>
39 #include <iostream>
40 #include <string>
41 
42 #include "log.h"
43 #include "dejagnu.h"
44 #include "rtmp.h"
45 #include "amf.h"
46 #include "check.h"
47 
48 using namespace amf;
49 using namespace gnash;
50 using namespace std;
51 
52 static void usage (void);
53 
54 static int verbosity;
55 
56 TestState runtest;
57 
58 // These are used to print more intelligent debug messages
59 const char *astype_str[] = {
60     "Number",
61     "Boolean",
62     "String",
63     "Object",
64     "MovieClip",
65     "Null",
66     "Undefined",
67     "Reference",
68     "ECMAArray",
69     "ObjectEnd",
70     "StrictArray",
71     "Date",
72     "LongString",
73     "Unsupported",
74     "Recordset",
75     "XMLObject",
76     "TypedObject"
77 };
78 
79 int
main(int argc,char * argv[])80 main(int argc, char *argv[])
81 {
82 
83     char buffer[300];
84     int c;
85 
86     //gnash::LogFile& dbglogfile = gnash::LogFile::getDefaultInstance();
87     //dbglogfile.setVerbosity(1);
88 
89     memset(buffer, 0, 300);
90 
91     while ((c = getopt (argc, argv, "hdvsm:")) != -1) {
92         switch (c) {
93           case 'h':
94             usage ();
95             break;
96 
97           case 'v':
98             verbosity++;
99             break;
100 
101           default:
102             usage ();
103             break;
104         }
105     }
106 
107     // get the file name from the command line
108     if (optind < argc) {
109         string filespec = argv[optind];
110         cout << "Will use \"" << filespec << "\" for test " << endl;
111     }
112 
113     AMF amf_obj;
114     int fd, ret;
115     unsigned char buf[AMF_PACKET_SIZE+1];
116     unsigned char *tmpptr;
117     AMF::amf_element_t el;
118 
119     // First see if we can read strings. This file is produced by
120     // using a network packet sniffer, and should be binary correct.
121     memset(buf, 0, AMF_PACKET_SIZE+1);
122     string filespec = SRCDIR;
123     filespec += "/connect-object.amf";
124 
125     fd = open(filespec.c_str(), O_RDONLY);
126     ret = read(fd, buf, AMF_PACKET_SIZE);
127     close(fd);
128 
129     amf_obj.parseHeader(buf);
130     if (amf_obj.getTotalSize() == 269) {
131         runtest.pass("Message Header Total Size");
132     } else {
133         runtest.fail("Message Header Total Size");
134     }
135 
136     if (amf_obj.getHeaderSize() == 12) {
137         runtest.pass("Message Header Size");
138     } else {
139         runtest.fail("Message Header Size");
140     }
141 
142     if (amf_obj.getMysteryWord() == 0) {
143         runtest.pass("Message Mystery Word");
144     } else {
145         runtest.fail("Message Mystery Word");
146     }
147 
148     if (amf_obj.getRouting() == 0) {
149         runtest.pass("Message Routing");
150     } else {
151         runtest.fail("Message Routing");
152     }
153 
154 //    amf_obj.parseBody(buf + amf_obj.getHeaderSize(), amf_obj.getTotalSize());
155 
156     // This extracts a "connect" message from the RTMP data stream. We
157     // look for everything ourselves to be the most accurate.
158     tmpptr = buf  + amf_obj.getHeaderSize();
159     int8_t *str = amf_obj.extractString(tmpptr);
160     if (strcmp(reinterpret_cast<const char *>(str), "connect") == 0) {
161         runtest.pass("Extracted \"connect\" string");
162     } else {
163         runtest.fail("Extracted \"connect\" string");
164     }
165 
166     tmpptr += strlen(reinterpret_cast<const char *>(str)) + AMF_HEADER_SIZE;
167     amfnum_t *num = amf_obj.extractNumber(tmpptr);
168     char     *numptr = (char *)num;
169     if ((numptr[6] == -16)
170         && (numptr[7] == 0x3f)) {
171         runtest.pass("Extracted \"connect\" number");
172     } else {
173         runtest.fail("Extracted \"connect\" number");
174     }
175     tmpptr += AMF_NUMBER_SIZE + 2;
176 
177     tmpptr = amf_obj.extractVariable(&el, tmpptr);
178     if (el.name == "app") {
179         runtest.pass("Extracted \"app\" variable");
180     } else {
181         runtest.fail("Extracted \"app\" variable");
182     }
183 //    cerr << el.name << endl;
184 
185     tmpptr = amf_obj.extractVariable(&el, tmpptr);
186     if (el.name == "flashVer") {
187         runtest.pass("Extracted \"flashVer\" variable");
188     } else {
189         runtest.fail("Extracted \"flashVer\" variable");
190     }
191 //    cerr << el.name << endl;
192 
193     tmpptr = amf_obj.extractVariable(&el, tmpptr);
194     if (el.name == "swfUrl") {
195         runtest.pass("Extracted \"swfUrl\" variable");
196     } else {
197         runtest.fail("Extracted \"swfUrl\" variable");
198     }
199 //    cerr << el.name << endl;
200 
201     tmpptr = amf_obj.extractVariable(&el, tmpptr);
202     if (el.name == "tcUrl") {
203         runtest.pass("Extracted \"tcUrl\" variable");
204     } else {
205         runtest.fail("Extracted \"tcUrl\" variable");
206     }
207 //    cerr << el.name << endl;
208 
209     tmpptr = amf_obj.extractVariable(&el, tmpptr);
210     if (el.name == "fpad") {
211         runtest.pass("Extracted \"fpad\" variable");
212     } else {
213         runtest.fail("Extracted \"fpad\" variable");
214     }
215 //    cerr << el.name << endl;
216 
217     tmpptr = amf_obj.extractVariable(&el, tmpptr);
218     if (el.name == "audioCodecs") {
219         runtest.pass("Extracted \"audioCodecs\" variable");
220     } else {
221         runtest.fail("Extracted \"audioCodecs\" variable");
222     }
223 //    cerr << el.name << endl;
224 
225     tmpptr = amf_obj.extractVariable(&el, tmpptr);
226     if (el.name == "videoCodecs") {
227         runtest.pass("Extracted \"videoCodecs\" variable");
228     } else {
229         runtest.fail("Extracted \"videoCodecs\" variable");
230     }
231 //    cerr << el.name << endl;
232 
233     tmpptr = amf_obj.extractVariable(&el, tmpptr);
234     if (el.name == "videoFunction") {
235         runtest.pass("Extracted \"videoFunction\" variable");
236     } else {
237         runtest.fail("Extracted \"videoFunction\" variable");
238     }
239 //    cerr << el.name << endl;
240 
241     tmpptr = amf_obj.extractVariable(&el, tmpptr);
242     if (el.name == "pageUrl") {
243         runtest.pass("Extracted \"pageURL\" variable");
244     } else {
245         runtest.fail("Extracted \"pageURL\" variable");
246     }
247 //    cerr << el.name << endl;
248 
249     amf_obj.extractVariable(&el, tmpptr);
250     if (el.name == "objectEncoding") {
251         runtest.pass("Extracted \"objectEncoding\" variable");
252     } else {
253         runtest.fail("Extracted \"objectEncoding\" variable");
254     }
255 //    cerr << el.name << endl;
256 
257     // Now build our own connect message with the same data, which
258     // should give us an exact copy.
259     int amf_index = amf_obj.getAMFIndex();
260     AMF::amf_headersize_e head_size = AMF::HEADER_12;
261     int total_size = amf_obj.getTotalSize();
262     AMF::content_types_e type = AMF::INVOKE;
263     amfsource_e routing = amf_obj.getRouting();
264     AMF rtmp;
265 
266     // First build and test the header. This uses the same data as the
267     // previous one
268     unsigned char *out = reinterpret_cast<unsigned char *>(rtmp.encodeRTMPHeader(amf_index, head_size, total_size, type, routing));
269     tmpptr = out;
270     rtmp.parseHeader(out);
271     if (rtmp.getTotalSize() == 269) {
272         runtest.pass("New Message Header Total Size");
273     } else {
274         runtest.fail("New Message Header Total Size");
275     }
276 
277     if (rtmp.getHeaderSize() == 12) {
278         runtest.pass("New Message Header Size");
279     } else {
280         runtest.fail("New Message Header Size");
281     }
282 
283     if (rtmp.getMysteryWord() == 0) {
284         runtest.pass("New Message Mystery Word");
285     } else {
286         runtest.fail("Message Mystery Word");
287     }
288 
289     if (rtmp.getRouting() == CLIENT) {
290         runtest.pass("New Message Routing");
291     } else {
292         runtest.fail("New Message Routing");
293     }
294 
295     check_equals(rtmp.getHeaderSize(), 12);
296 
297     if (memcmp(out, buf, 12) == 0) {
298         runtest.pass("RTMP Headers match");
299     } else {
300         size_t s = 12;
301         runtest.fail("RTMP Headers mismatch");
302         cerr << "buf is: 0x" << hexify(buf, s, true) << endl;
303         cerr << "out is: 0x" << hexify(out, s, true) << endl;
304     }
305 
306     tmpptr += rtmp.getHeaderSize();
307 
308     // Now build up a body of a connect message
309     unsigned char *var;
310 
311     var = (unsigned char *)rtmp.encodeString("connect");
312     int8_t *c_out = rtmp.extractString(var);
313     if ( ! c_out )
314     {
315         runtest.fail("Encoded \"connect\" string could not be extracted");
316     }
317     else
318     {
319         std::string s_in("connect");
320         std::string s_out(reinterpret_cast<const char *>(c_out));
321 
322         if (s_in == s_out) {
323             runtest.pass("Encoded \"connect\" string");
324         } else {
325             runtest.fail("Encoded \"connect\" string");
326             cerr << "Encoded 'connect' returned as as" << s_out << endl;
327         }
328     }
329     tmpptr = rtmp.appendPtr(tmpptr, var, strlen("connect") + 3);
330     delete [] var;
331 
332     amfnum_t bignum = 0x3ff0000000000000LL;
333     numptr = (char *)&bignum;
334     var = (unsigned char *)rtmp.encodeNumber(bignum);
335     if (*rtmp.extractNumber(var) == bignum) {
336         runtest.pass("Encoded \"connect\" number");
337     } else {
338         runtest.fail("Encoded \"connect\" number");
339     }
340 
341     tmpptr = rtmp.appendPtr(tmpptr, var, AMF_NUMBER_SIZE + 1);
342     delete [] var;
343 
344     // Start the object
345     *tmpptr++ = AMF::OBJECT;
346 
347     var = (unsigned char *)rtmp.encodeVariable("app", "oflaDemo");
348     rtmp.extractVariable(&el, var);
349     if ((el.name == "app") && (strncmp((char *)el.data, "oflaDemo", 8) == 0)) {
350         runtest.pass("Encoded \"app\" variable");
351     } else {
352         runtest.fail("Encoded \"app\" variable");
353     }
354     tmpptr = rtmp.appendPtr(tmpptr, var, el.length + strlen("app") + 5);
355     delete [] var;
356 
357     var = (unsigned char *)rtmp.encodeVariable("flashVer", "LNX 9,0,31,0");
358     rtmp.extractVariable(&el, var);
359     if ((el.name == "flashVer") && (strncmp((char *)el.data, "LNX 9,0,31,0", el.length) == 0)) {
360         runtest.pass("Encoded \"flashVer\" variable");
361     } else {
362         runtest.fail("Encoded \"flashVer\" variable");
363     }
364     tmpptr = rtmp.appendPtr(tmpptr, var, el.length + strlen("flashVer") + 5);
365     delete [] var;
366 
367     var = (unsigned char *)rtmp.encodeVariable("swfUrl", "http://www.red5.nl/tools/publisher/publisher.swf");
368     rtmp.extractVariable(&el, var);
369     if ((el.name == "swfUrl") && (strncmp((char *)el.data, "http://www.red5.nl/tools/publisher/publisher.swf", el.length) == 0)) {
370         runtest.pass("Encoded \"swfUrl\" variable");
371     } else {
372         runtest.fail("Encoded \"swfUrl\" variable");
373     }
374     tmpptr = rtmp.appendPtr(tmpptr, var, el.length + strlen("swfUrl") + 5);
375     delete [] var;
376 
377     var = (unsigned char *)rtmp.encodeVariable("tcUrl", "rtmp://localhost/oflaDemo");
378     rtmp.extractVariable(&el, var);
379     if ((el.name == "tcUrl") && (strncmp((char *)el.data, "rtmp://localhost/oflaDemo", 25) == 0)) {
380         runtest.pass("Encoded \"tcUrl\" variable");
381     } else {
382         runtest.fail("Encoded \"tcUrl\" variable");
383     }
384     tmpptr = rtmp.appendPtr(tmpptr, var, el.length + strlen("tcUrl") + 5);
385     delete [] var;
386 
387     var = (unsigned char *)rtmp.encodeVariable("fpad", false);
388     rtmp.extractVariable(&el, var);
389     if ((el.name == "fpad") && (*el.data == 0)) {
390         runtest.pass("Encoded \"fpad\" Boolean variable");
391     } else {
392         runtest.fail("Encoded \"fpad\" Boolean variable");
393     }
394     tmpptr = rtmp.appendPtr(tmpptr, var, 1 + strlen("fpad") + 3);
395     delete [] var;
396 
397     bignum = 0x388340LL;
398     numptr = (char *)&bignum;
399     var = (unsigned char *)rtmp.encodeVariable("audioCodecs", bignum);
400     rtmp.extractVariable(&el, var);
401 
402     if ((el.type == amf::AMF::NUMBER)
403         && (el.name == "audioCodecs")
404         && (el.data[5] == 0x38)
405         && (el.data[6] == 0x83)
406         && (el.data[7] == 0x40)) {
407         runtest.pass("Encoded \"audioCodecs\" variable");
408     } else {
409         runtest.fail("Encoded \"audioCodecs\" variable");
410     }
411     tmpptr = rtmp.appendPtr(tmpptr, var, el.name.size() + AMF_NUMBER_SIZE + 3);
412     delete [] var;
413 
414     bignum = 0x5f40LL;
415     numptr = (char *)&bignum;
416     var = (unsigned char *)rtmp.encodeVariable("videoCodecs", bignum);
417     rtmp.extractVariable(&el, var);
418 
419     if ((el.type == amf::AMF::NUMBER)
420         && (el.name == "videoCodecs")
421         && (el.data[6] == 0x5f)
422         && (el.data[7] == 0x40)) {
423         runtest.pass("Encoded \"videoCodecs\" variable");
424     } else {
425         runtest.fail("Encoded \"videoCodecs\" variable");
426     }
427     tmpptr = rtmp.appendPtr(tmpptr, var, el.name.size() + AMF_NUMBER_SIZE + 3);
428     delete [] var;
429 
430     bignum = 0xf03fLL;
431     numptr = (char *)&bignum;
432     var = (unsigned char *)rtmp.encodeVariable("videoFunction", bignum);
433     rtmp.extractVariable(&el, var);
434 
435     if ((el.type == amf::AMF::NUMBER)
436         && (el.name == "videoFunction")
437         && (el.data[6] == 0xf0)
438         && (el.data[7] == 0x3f)) {
439         runtest.pass("Encoded \"videoFunction\" variable");
440     } else {
441         runtest.fail("Encoded \"videoFunction\" variable");
442     }
443     tmpptr = rtmp.appendPtr(tmpptr, var, el.name.size() + AMF_NUMBER_SIZE + 3);
444     delete [] var;
445 
446     var = (unsigned char *)rtmp.encodeVariable("pageUrl");
447     rtmp.extractVariable(&el, var);
448     if ((el.type == amf::AMF::UNDEFINED)
449         && (el.name == "pageUrl")) {
450         runtest.pass("Encoded \"pageUrl\" undefined variable");
451     } else {
452         runtest.fail("Encoded \"pageUrl\" undefined variable");
453     }
454     tmpptr = rtmp.appendPtr(tmpptr, var, el.name.size() + 3);
455     delete [] var;
456 
457     bignum = 0x0;
458     numptr = (char *)&bignum;
459     var = (unsigned char *)rtmp.encodeVariable("objectEncoding", bignum);
460     rtmp.extractVariable(&el, var);
461 
462     if ((el.type == amf::AMF::NUMBER)
463         && (el.name == "objectEncoding")
464         && (el.data[6] == 0x0)
465         && (el.data[7] == 0x0)) {
466         runtest.pass("Encoded \"objectEncoding\" variable");
467     } else {
468         runtest.fail("Encoded \"objectEncoding\" variable");
469     }
470     tmpptr = rtmp.appendPtr(tmpptr, var, el.name.size() + AMF_NUMBER_SIZE + 3);
471     delete [] var;
472 
473     // Start the object
474     *tmpptr++ = AMF::OBJECT_END;
475 
476     if (memcmp(buf, out, amf_obj.getTotalSize()) == 0) {
477         runtest.pass("Object Packets match");
478     } else {
479         runtest.fail("Object Packets mismatch");
480     }
481 
482     size_t hexsize = std::max(AMF_PACKET_SIZE, amf_obj.getTotalSize())*2;
483     cerr << "buf is: 0x" << hexify(buf, amf_obj.getTotalSize() + 10, true) << ", size is: " << amf_obj.getTotalSize() << endl;
484     cerr << "out is: 0x" << hexify(out, rmtp.getTotalSize() + 10, true) << ", size is: " << rtmp.getTotalSize() << endl;
485 
486 //    delete out;
487 }
488 
489 static void
usage(void)490 usage (void)
491 {
492     cerr << "This program tests Shared Object support in the AMF library." << endl;
493     cerr << "Usage: test_object [hv]" << endl;
494     cerr << "-h\tHelp" << endl;
495     cerr << "-v\tVerbose" << endl;
496     exit (-1);
497 }
498 
499 #else
500 
501 int
main(int,char)502 main(int /*argc*/, char /* *argv[]*/)
503 {
504   // nop
505   return 0;
506 }
507 #endif
508