1 // rtmp.cpp:  Adobe/Macromedia Real Time Message Protocol handler, for Gnash.
2 //
3 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 //   Free Software 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 <ctime>
26 #include <iostream>
27 #include <string>
28 #include <map>
29 
30 #if ! (defined(_WIN32) || defined(WIN32))
31 #	include <netinet/in.h>
32 #endif
33 
34 #include <boost/lexical_cast.hpp>
35 #include "log.h"
36 #include "rc.h"
37 #include "amf.h"
38 #include "rtmp.h"
39 #include "rtmp_client.h"
40 #include "network.h"
41 #include "element.h"
42 // #include "handler.h"
43 #include "utility.h"
44 #include "buffer.h"
45 #include "GnashSleep.h"
46 #include "URL.h"
47 
48 typedef std::shared_ptr<cygnal::Element> ElementSharedPtr;
49 
50 namespace gnash
51 {
52 
53 extern const char *ping_str[];
54 
55 // The rcfile is loaded and parsed here:
56 static RcInitFile& rcfile = RcInitFile::getDefaultInstance();
57 
58 // extern map<int, Handler *> handlers;
59 
RTMPClient()60 RTMPClient::RTMPClient()
61     : _connected(false),
62       _connections(0)
63 {
64 //    GNASH_REPORT_FUNCTION;
65 }
66 
~RTMPClient()67 RTMPClient::~RTMPClient()
68 {
69 //    GNASH_REPORT_FUNCTION;
70     _connected = false;
71 
72     _properties.clear();
73 //    delete _body;
74 }
75 
76 
77 // These are used for creating the primary objects
78 
79 // Make the NetConnection object that is used to connect to the
80 // server.
81 std::shared_ptr<cygnal::Buffer>
encodeConnect()82 RTMPClient::encodeConnect()
83 {
84 //     GNASH_REPORT_FUNCTION;
85 
86     return encodeConnect(_path.c_str());
87 }
88 
89 std::shared_ptr<cygnal::Buffer>
encodeConnect(const char * uri)90 RTMPClient::encodeConnect(const char *uri)
91 {
92 //     GNASH_REPORT_FUNCTION;
93 
94     return encodeConnect(uri, RTMPClient::DEFAULT_AUDIO_SET,
95 			 RTMPClient::DEFAULT_VIDEO_SET,
96 			 RTMPClient::SEEK);
97 }
98 
99 std::shared_ptr<cygnal::Buffer>
encodeConnect(const char * uri,double audioCodecs,double videoCodecs,double videoFunction)100 RTMPClient::encodeConnect(const char *uri,
101 			  double audioCodecs, double videoCodecs,
102 			  double videoFunction)
103 {
104     GNASH_REPORT_FUNCTION;
105     using std::string;
106 
107     URL url(uri);
108     string portstr;
109 
110     short port = 0;
111     string protocol;		// the network protocol, rtmp or http
112     string query;		// any queries for the host
113     string app;			// the application name
114     string path;		// the path to the file on the server
115     string tcUrl;		// the tcUrl field
116     string swfUrl;		// the swfUrl field
117     string filename;		// the filename to play
118     string pageUrl;		// the pageUrl field
119     string hostname;		// the hostname of the server
120 
121 
122     protocol = url.protocol();
123     hostname = url.hostname();
124     portstr = url.port();
125     query = url.querystring();
126 
127     if (portstr.empty()) {
128         if ((protocol == "http") || (protocol == "rtmpt")) {
129             port = RTMPT_PORT;
130         }
131         if (protocol == "rtmp") {
132             port = RTMP_PORT;
133         }
134     } else {
135         port = strtol(portstr.c_str(), nullptr, 0) & 0xffff;
136     }
137 
138 
139     path = url.path();
140 
141     string::size_type end = path.rfind('/');
142     if (end != string::npos) {
143 	filename = path.substr(end + 1);
144     }
145 
146     tcUrl = uri;
147     app = filename;
148     swfUrl = "http://localhost:1935/demos/videoConference.swf";
149     pageUrl = "http://gnashdev.org";
150 
151     log_network(_("URL is %s"), url);
152     log_network(_("Protocol is %s"), protocol);
153     log_network(_("Host is %s"), hostname);
154     log_network(_("Port is %s"), port);
155     log_network(_("Path is %s"), path);
156     log_network(_("Filename is %s"), filename);
157     log_network(_("App is %s"), app);
158     log_network(_("Query is %s"), query);
159     log_network(_("tcUrl is %s"), tcUrl);
160     log_network(_("swfUrl is %s"), swfUrl);
161     log_network(_("pageUrl is %s"), pageUrl);
162 
163     return encodeConnect(app.c_str(), swfUrl.c_str(), tcUrl.c_str(),
164 			 audioCodecs, videoCodecs, videoFunction,
165 			 pageUrl.c_str());
166 }
167 
168 std::shared_ptr<cygnal::Buffer>
encodeConnect(const char * app,const char * swfUrl,const char * tcUrl,double audioCodecs,double videoCodecs,double videoFunction,const char * pageUrl)169 RTMPClient::encodeConnect(const char *app, const char *swfUrl, const char *tcUrl,
170                           double audioCodecs, double videoCodecs, double videoFunction,
171                           const char *pageUrl)
172 {
173     GNASH_REPORT_FUNCTION;
174 
175     cygnal::AMF amf_obj;
176 
177     ElementSharedPtr connect(new cygnal::Element);
178     connect->makeString("connect");
179 
180     ElementSharedPtr connum(new cygnal::Element);
181     // update the counter for the number of connections. This number is used heavily
182     // in RTMP to help keep communications clear when there are multiple streams.
183     _connections++;
184     connum->makeNumber(_connections);
185 
186     // Make the top level object
187     ElementSharedPtr obj(new cygnal::Element);
188     obj->makeObject();
189 
190     ElementSharedPtr appnode(new cygnal::Element);
191     appnode->makeString("app", app);
192     obj->addProperty(appnode);
193 
194     const char *version = nullptr;
195     if (rcfile.getFlashVersionString().size() > 0) {
196         version = rcfile.getFlashVersionString().c_str();
197     } else {
198         version = "LNX 9,0,31,0";
199     }
200 
201     ElementSharedPtr flashVer(new cygnal::Element);
202     flashVer->makeString("flashVer", version);
203     obj->addProperty(flashVer);
204 
205     ElementSharedPtr swfUrlnode(new cygnal::Element);
206 //    swfUrl->makeString("swfUrl", "http://192.168.1.70/software/gnash/tests/ofla_demo.swf");
207     swfUrlnode->makeString("swfUrl", swfUrl);
208     obj->addProperty(swfUrlnode);
209 
210 //    filespec = "rtmp://localhost:5935/oflaDemo";
211     ElementSharedPtr tcUrlnode(new cygnal::Element);
212     tcUrlnode->makeString("tcUrl", tcUrl);
213     obj->addProperty(tcUrlnode);
214 
215     ElementSharedPtr fpad(new cygnal::Element);
216     fpad->makeBoolean("fpad", false);
217     obj->addProperty(fpad);
218 
219     ElementSharedPtr audioCodecsnode(new cygnal::Element);
220 //    audioCodecsnode->makeNumber("audioCodecs", 615);
221     audioCodecsnode->makeNumber("audioCodecs", audioCodecs);
222     obj->addProperty(audioCodecsnode);
223 
224     ElementSharedPtr videoCodecsnode(new cygnal::Element);
225 //    videoCodecsnode->makeNumber("videoCodecs", 124);
226     videoCodecsnode->makeNumber("videoCodecs", videoCodecs);
227     obj->addProperty(videoCodecsnode);
228 
229     ElementSharedPtr videoFunctionnode(new cygnal::Element);
230 //    videoFunctionnode->makeNumber("videoFunction", 0x1);
231     videoFunctionnode->makeNumber("videoFunction", videoFunction);
232     obj->addProperty(videoFunctionnode);
233 
234     ElementSharedPtr pageUrlnode(new cygnal::Element);
235 //    pageUrlnode->makeString("pageUrl", "http://x86-ubuntu/software/gnash/tests/");
236     pageUrlnode->makeString("pageUrl", pageUrl);
237     obj->addProperty(pageUrlnode);
238 
239 #if 0
240     ElementSharedPtr objencodingnode(new Element);
241     objencodingnode->makeNumber("objectEncoding", 0.0);
242     obj->addProperty(objencodingnode);
243 #endif
244 //    size_t total_size = 227;
245 //     Buffer *out = encodeHeader(0x3, RTMP::HEADER_12, total_size,
246 //                                      RTMP::INVOKE, RTMP::FROM_CLIENT);
247 //     const char *rtmpStr = "03 00 00 04 00 01 1f 14 00 00 00 00";
248 //     Buffer *rtmpBuf = hex2mem(rtmpStr);
249     std::shared_ptr<cygnal::Buffer> conobj = connect->encode();
250     std::shared_ptr<cygnal::Buffer> numobj = connum->encode();
251     std::shared_ptr<cygnal::Buffer> encobj = obj->encode();
252 
253     std::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(conobj->size() + numobj->size() + encobj->size()));
254     *buf += conobj;
255     *buf += numobj;
256     *buf += encobj;
257 
258     return buf;
259 }
260 
261 bool
connectToServer(const std::string & url)262 RTMPClient::connectToServer(const std::string &url)
263 {
264     GNASH_REPORT_FUNCTION;
265 
266     URL uri(url);
267 
268     // If we're currently not connected, build and send the
269     // initial handshake packet.
270     if (connected() == false) {
271 	short port = strtol(uri.port().c_str(), nullptr, 0) & 0xffff;
272 	if (!createClient(uri.hostname(), port)) {
273 	    return false;
274 	}
275 
276 	// Build the NetConnection Packet, which seems to need
277 	// to be on the end of the second block of handshake data.
278 	// We build this here so we can get the total encoded
279 	// size of the object.
280 	std::shared_ptr<cygnal::Buffer> ncbuf = encodeConnect();
281 
282 	// As at this point we don't have an RTMP connection,
283 	// we can't use the regular sendMsg(), that handles the RTMP
284 	// headers for continuation packets. We know a this point it's
285 	// always one by, so we just add it by hand. It doesn't matter
286 	// as long as the channel number matches the one used to
287 	// create the initial RTMP packet header.
288 	std::unique_ptr<cygnal::Buffer> newbuf(new cygnal::Buffer(ncbuf->size() + 5));
289 	size_t nbytes = 0;
290 	size_t chunk = RTMP_VIDEO_PACKET_SIZE;
291 	do {
292 	    // The last packet is smaller
293 	    if ((ncbuf->allocated() - nbytes) < static_cast<size_t>(RTMP_VIDEO_PACKET_SIZE)) {
294 		chunk = ncbuf->allocated() - nbytes;
295 	    }
296 	    newbuf->append(ncbuf->reference() + nbytes, chunk);
297  	    nbytes  += chunk;
298 	    if (chunk == static_cast<size_t>(RTMP_VIDEO_PACKET_SIZE)) {
299 		std::uint8_t headone = 0xc3;
300 		*newbuf += headone;
301 	    }
302 	} while (nbytes < ncbuf->allocated());
303 
304 	std::shared_ptr<cygnal::Buffer> head = encodeHeader(0x3,
305 			    RTMP::HEADER_12, ncbuf->allocated(),
306 			    RTMP::INVOKE, RTMPMsg::FROM_CLIENT);
307 
308 	// Build the first handshake packet, and send it to the
309 	// server.
310 	std::shared_ptr<cygnal::Buffer> handshake1 = handShakeRequest();
311 	if (!handshake1) {
312 	    log_error(_("RTMP handshake request failed"));
313 	    return false;
314 	}
315 
316 	std::unique_ptr<cygnal::Buffer> handshake2(new cygnal::Buffer
317 		  ((RTMP_HANDSHAKE_SIZE * 2) + newbuf->allocated()
318 		   + RTMP_MAX_HEADER_SIZE));
319 
320 	// Finish the handshake process, which has to have the
321 	// NetConnection::connect() as part of the buffer, or Red5
322 	// refuses to answer.
323 	setTimeout(20);
324 #if 0
325 	*handshake2 = handshake1;
326 	*handshake2 += head;
327  	*handshake2 += ncbuf;
328 	if (!clientFinish(*handshake2)) {
329 #else
330 	*handshake2 = head;
331 	handshake2->append(newbuf->reference(), newbuf->allocated());
332 	handshake2->dump();
333         if (!clientFinish(*handshake2)) {
334 #endif
335 	    log_error(_("RTMP handshake completion failed!"));
336 //	    return (false);
337 	}
338 
339 	// give the server time to process our NetConnection::connect() request
340 	std::shared_ptr<cygnal::Buffer> response;
341 	std::shared_ptr<RTMP::rtmp_head_t> rthead;
342 	std::shared_ptr<RTMP::queues_t> que;
343 
344 	RTMPClient::msgque_t msgque = recvResponse();
345 	while (msgque.size()) {
346 	    std::shared_ptr<RTMPMsg> msg = msgque.front();
347 	    msgque.pop_front();
348 	    if (msg->getStatus() ==  RTMPMsg::NC_CONNECT_SUCCESS) {
349 		log_network(_("Sent NetConnection Connect message successfully"));
350 	    }
351 	    if (msg->getStatus() ==  RTMPMsg::NC_CONNECT_FAILED) {
352 		log_error(_("Couldn't send NetConnection Connect message,"));
353 	    }
354 	}
355     }
356 
357     return true;
358 }
359 
360 std::shared_ptr<cygnal::Buffer>
361 RTMPClient::encodeEchoRequest(const std::string &method, double id, cygnal::Element &el)
362 {
363 //    GNASH_REPORT_FUNCTION;
364     std::shared_ptr<cygnal::Element> str(new cygnal::Element);
365     str->makeString(method);
366     std::shared_ptr<cygnal::Buffer> strobj = str->encode();
367 
368     // Encod ethe stream ID
369     std::shared_ptr<cygnal::Element>  num(new cygnal::Element);
370     num->makeNumber(id);
371     std::shared_ptr<cygnal::Buffer> numobj = num->encode();
372 
373     // Set the NULL object element that follows the stream ID
374     std::shared_ptr<cygnal::Element> null(new cygnal::Element);
375     null->makeNull();
376     std::shared_ptr<cygnal::Buffer> nullobj = null->encode();
377 
378     std::shared_ptr<cygnal::Buffer> elobj = el.encode();
379 
380     size_t totalsize = strobj->size() + numobj->size() + nullobj->size() + elobj->size();
381 
382     std::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(totalsize));
383 
384     *buf += strobj;
385     *buf += numobj;
386     *buf += nullobj;
387     *buf += elobj;
388 
389     return buf;
390 }
391 
392 // 43 00 1a 21 00 00 19 14 02 00 0c 63 72 65 61 74  C..!.......creat
393 // 65 53 74 72 65 61 6d 00 40 08 00 00 00 00 00 00  eStream.@.......
394 // 05                                                    .
395 std::shared_ptr<cygnal::Buffer>
396 RTMPClient::encodeStream(double id)
397 {
398 //    GNASH_REPORT_FUNCTION;
399 
400     struct timespec now;
401     clock_gettime (CLOCK_REALTIME, &now);
402 
403     std::shared_ptr<cygnal::Element> str(new cygnal::Element);
404     str->makeString("createStream");
405     std::shared_ptr<cygnal::Buffer> strobj = str->encode();
406 
407     std::shared_ptr<cygnal::Element>  num(new cygnal::Element);
408     num->makeNumber(id);
409     std::shared_ptr<cygnal::Buffer> numobj = num->encode();
410 
411     // Set the NULL object element that follows the stream ID
412     std::shared_ptr<cygnal::Element> null(new cygnal::Element);
413     null->makeNull();
414     std::shared_ptr<cygnal::Buffer> nullobj = null->encode();
415 
416     size_t totalsize = strobj->size() + numobj->size() + nullobj->size();
417 
418     std::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(totalsize));
419 
420     *buf += strobj;
421     *buf += numobj;
422     *buf += nullobj;
423 
424     return buf;
425 }
426 
427 // 127.0.0.1:38167 -> 127.0.0.1:1935 [AP]
428 // 08 00 1b 1b 00 00 2a 14 01 00 00 00 02 00 04 70  ......*........p
429 // 6c 61 79 00 00 00 00 00 00 00 00 00 05 02 00 16  lay.............
430 // 6f 6e 32 5f 66 6c 61 73 68 38 5f 77 5f 61 75 64  on2_flash8_w_aud
431 // 69 6f 2e 66 6c 76 c2 00 03 00 00 00 01 00 00 27  io.flv.........'
432 // 10
433 std::shared_ptr<cygnal::Buffer>
434 RTMPClient::encodeStreamOp(double id, rtmp_op_e op, bool flag)
435 {
436 //    GNASH_REPORT_FUNCTION;
437     return encodeStreamOp(id, op, flag, "", 0);
438 }
439 
440 std::shared_ptr<cygnal::Buffer>
441 RTMPClient::encodeStreamOp(double id, rtmp_op_e op, bool flag, double pos)
442 {
443 //    GNASH_REPORT_FUNCTION;
444     return encodeStreamOp(id, op, flag, "", pos);
445 }
446 
447 std::shared_ptr<cygnal::Buffer>
448 RTMPClient::encodeStreamOp(double id, rtmp_op_e op, bool flag, const std::string &name)
449 {
450 //    GNASH_REPORT_FUNCTION;
451     return encodeStreamOp(id, op, flag, name, 0);
452 }
453 
454 // A seek packet is the operation name "seek", followed by the
455 // stream ID, then a NULL object, followed by the location to seek to.
456 //
457 // A pause packet is the operation name "pause", followed by the stream ID,
458 // then a NULL object, a boolean (always true from what I can tell), and then
459 // a location, which appears to always be 0.
460 std::shared_ptr<cygnal::Buffer>
461 RTMPClient::encodeStreamOp(double id, rtmp_op_e op, bool flag, const std::string &name, double pos)
462 {
463 //    GNASH_REPORT_FUNCTION;
464 
465     // Set the operations command name
466     cygnal::Element str;
467     switch (op) {
468       case STREAM_PLAY:		// play the existing stream
469 	  str.makeString("play");
470 	  break;
471       case STREAM_PAUSE:	// pause the existing stream
472 	  str.makeString("pause");
473 	  break;
474       case STREAM_PUBLISH:	// publish the existing stream
475 	  str.makeString("publish");
476 	  break;
477       case STREAM_STOP:		// stop the existing stream
478 	  str.makeString("stop");
479 	  break;
480       case STREAM_SEEK:		// seek in the existing stream
481 	  str.makeString("seek");
482 	  break;
483       default:
484 	  std::shared_ptr<cygnal::Buffer> foo;
485 	  return foo;
486     };
487 
488     std::shared_ptr<cygnal::Buffer> strobj = str.encode();
489 
490     // Set the stream ID, which follows the command
491     cygnal::Element strid;
492     strid.makeNumber(id);
493     std::shared_ptr<cygnal::Buffer> stridobj = strid.encode();
494 
495     // Set the NULL object element that follows the stream ID
496     cygnal::Element null;
497     null.makeNull();
498     std::shared_ptr<cygnal::Buffer> nullobj = null.encode();
499 
500     // Set the BOOLEAN object element that is the last field in the packet
501     // (SEEK and PLAY don't use the boolean flag)
502     std::shared_ptr<cygnal::Buffer> boolobj;
503     if ((op != STREAM_SEEK) && (op != STREAM_PLAY)) {
504         cygnal::Element boolean;
505         boolean.makeBoolean(flag);
506         boolobj = boolean.encode();
507     }
508 
509     // The seek command also may have an optional location to seek to
510     std::shared_ptr<cygnal::Buffer> posobj;
511     if ((op == STREAM_PAUSE) || (op == STREAM_SEEK)) {
512         cygnal::Element seek;
513         seek.makeNumber(pos);
514         posobj = seek.encode();
515     }
516 
517     // The play command has an optional field, which is the name of the file
518     // used for the stream. A Play command without this name set play an
519     // existing stream that is already open.
520     std::shared_ptr<cygnal::Buffer> fileobj;
521     if (!name.empty()) {
522         cygnal::Element filespec;
523         filespec.makeString(name);
524         fileobj = filespec.encode();
525     }
526 
527     // Calculate the packet size, rather than use the default as we want to
528     // to be concious of the memory usage. The command name and the optional
529     // file name are the only two dynamically sized fields.
530     size_t pktsize = strobj->size() + stridobj->size() + nullobj->size();
531     if ( boolobj ) pktsize += boolobj->size();
532     if ( fileobj ) pktsize += fileobj->size();
533     if ( posobj ) pktsize += posobj->size();
534 
535     std::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(pktsize));
536     *buf += strobj;
537     *buf += stridobj;
538     *buf += nullobj;
539     if ( boolobj ) *buf += boolobj;
540     if ( fileobj ) *buf += fileobj;
541     if ( posobj ) *buf += posobj;
542 
543     return buf;
544 }
545 
546 // A request for a handshake is initiated by sending a byte with a
547 // value of 0x3, followed by a message body of unknown format.
548 std::shared_ptr<cygnal::Buffer>
549 RTMPClient::handShakeRequest()
550 {
551     GNASH_REPORT_FUNCTION;
552     std::uint32_t zero = 0;
553 
554     // Make a buffer to hold the handshake data.
555     std::shared_ptr<cygnal::Buffer> handshake(new cygnal::Buffer(RTMP_HANDSHAKE_SIZE+1));
556     if (!handshake) {
557 	return handshake;
558     }
559 
560     // All RTMP connections start with the RTMP version number
561     // which should always be 0x3.
562     *handshake = RTMP_VERSION;
563 
564     *handshake += RTMP::getTime();
565 
566     // This next field in the header for the RTMP handshake should be zeros
567     *handshake += zero;
568 
569     // The handshake contains random data after the initial header
570     for (int i=0; i<RTMP_RANDOM_SIZE; i++) {
571 	std::uint8_t pad = i^256;
572         *handshake += pad;
573     }
574 
575     int ret = writeNet(*handshake);
576     if (ret <= 0) {
577 	handshake.reset();
578     }
579 
580     return handshake;
581 }
582 
583 // The client finishes the handshake process by sending the second
584 // data block we get from the server as the response
585 
586 std::shared_ptr<cygnal::Buffer>
587 RTMPClient::clientFinish()
588 {
589 //     GNASH_REPORT_FUNCTION;
590 
591     cygnal::Buffer data;
592     return clientFinish(data);
593 }
594 
595 std::shared_ptr<cygnal::Buffer>
596 RTMPClient::clientFinish(cygnal::Buffer &data)
597 {
598     GNASH_REPORT_FUNCTION;
599     bool done = false;
600     int ret = 0;
601     int retries = 5;
602     int offset = 0;
603 
604     // Create the initial buffer to hold the response, and keep reading data
605     // until it is populated
606     // The handhake for this phase is twice the size of the initial handshake
607     // we sent previously, plus one byte for the RTMP version header.
608     int max_size = (RTMP_HANDSHAKE_SIZE*2) + 1;
609     std::shared_ptr<cygnal::Buffer> handshake1(new cygnal::Buffer(
610 			      max_size + data.allocated()));
611     do {
612 	ret = readNet(handshake1->end(), max_size - offset);
613 	offset += ret;
614 	handshake1->setSeekPointer(handshake1->reference() + offset);
615 	if ((offset >= max_size) || (ret >= max_size)) {
616 	    handshake1->setSeekPointer(handshake1->reference() + max_size);
617 // 	    log_network("Read entire packet of %d bytes", ret);
618 	    done = true;
619 	}
620 	if (ret < 0) {
621 	    log_error(_("Couldn't read data block in handshake!"));
622 	    handshake1.reset();
623 	    return handshake1;
624 	}
625 	// if retries equals zero, then we're done trying
626 	if (retries == 0) {
627 	    done = true;
628 	} else {
629 	    --retries;
630 	}
631     } while (!done);
632 
633     if (handshake1->allocated() == boost::lexical_cast<size_t>(max_size)) {
634 	log_network(_("Read data block in handshake, got %d bytes."),
635 		   handshake1->allocated());
636     } else {
637 	log_error(_("Couldn't read data block in handshake, read %d bytes!"),
638 		  handshake1->allocated());
639     }
640 
641     _handshake_header.uptime = ntohl(*reinterpret_cast<std::uint32_t *>
642 				     (handshake1->reference() + 1));
643 
644     log_network(_("RTMP Handshake header: Uptime: %u"),
645 		_handshake_header.uptime);
646 
647 #if 0
648     if (memcmp(handshake2->reference() + RTMP_HANDSHAKE_SIZE + 8,
649 	       _handshake->reference() + 8, RTMP_RANDOM_SIZE-8) == 0) {
650 	log_network("Handshake matched");
651     } else {
652 	log_network("Handshake didn't match");
653 // 	return false;
654     }
655 #endif
656 
657     // Make a new buffer big enough to hold the handshake, data, and header byte
658     std::shared_ptr<cygnal::Buffer> handshake2(new cygnal::Buffer(
659 			     RTMP_HANDSHAKE_SIZE + data.allocated()));
660 
661     // Copy the timestamp from the message we just received.
662     handshake2->copy(handshake1->reference()+1, sizeof(std::uint32_t));
663 
664 #if 1
665     // The next timestamp is the one we just received bumped up a tiny bit.
666     // I have no clue if this is correct, but fom hex dumps the previous
667     // timestamp should be the baseline, and this is just that time plus
668     // whatever it took to get the message. The 7 is a bit randomly chosen.
669     std::uint32_t tt = htonl(_handshake_header.uptime + 7);
670 #else
671     // Get the uptime for the header
672     // yes, we loose precision here but it's only a 4 byte field
673     time_t t;
674     time(&t);
675     std::uint32_t tt = t;
676 #endif
677     *handshake2 += tt;
678 
679     // Add the handshake data
680     std::uint8_t *start = handshake1->reference() + RTMP_HANDSHAKE_SIZE
681 	+ 1 + 8;
682     handshake2->append(start, RTMP_RANDOM_SIZE);
683     // Add the NetConnection::connect() packet
684     *handshake2 += data;
685 
686     // Write the second chunk to the server
687     log_network(_("About to write %d bytes, data is: %d bytes."),
688 	      handshake2->allocated(),
689 	      data.allocated());
690     log_network(_("Client response header for handshake 2: %s"),
691 		hexify(handshake2->reference(), 12, false));
692     log_network(_("Data in response for handshake 2: %s"),
693 		hexify(handshake1->reference() + RTMP_HANDSHAKE_SIZE + 1, 12, false));
694 #if 0
695     ret = writeNet(handshake2->reference()+RTMP_HANDSHAKE_SIZE,
696 		   RTMP_HANDSHAKE_SIZE + data.allocated() + 1);
697 #else
698     ret = writeNet(*handshake2);
699 #endif
700     if ( ret <= 0 ) {
701 	log_error(_("Couldn't write the second handshake packet!"));
702 	handshake1.reset();
703 	return handshake1;
704     } else {
705 	_connected = false;
706     }
707 
708     // Since the handshake completed successfully, we're connected.
709     _connected = true;
710 
711     return handshake1;
712 }
713 
714 // Get and process an RTMP response. After reading all the data, then we have
715 // split it up on the chunksize boudaries, and into the respective queues
716 // for each channel.
717 RTMPClient::msgque_t
718 RTMPClient::recvResponse()
719 {
720     GNASH_REPORT_FUNCTION;
721 
722     RTMPClient::msgque_t msgque;
723 
724     // Read the responses back from the server.  This is usually a series of system
725     // messages on channel 2, and the response message on channel 3 from our request.
726     std::shared_ptr<cygnal::Buffer> response = recvMsg();
727     if (!response) {
728 	log_error(_("Got no response from the RTMP server"));
729 	return msgque;
730     }
731 
732     // when doing remoting calls I don't see this problem with an empty packet from Red5,
733     // but when I do streaming, it's always there, so we need to remove it.
734     std::uint8_t *pktstart = response->reference();
735     if (*pktstart == 0xff) {
736 	log_network(_("Got empty packet in buffer."));
737 	pktstart++;
738     }
739 
740     // The response packet contains multiple messages for multiple channels, so we
741     // we have to split the Buffer into seperate messages on a chunksize boundary.
742     std::shared_ptr<RTMP::rtmp_head_t> rthead;
743     std::shared_ptr<RTMP::queues_t> que = split(pktstart, response->allocated()-1);
744 
745     // If we got no responses, something obviously went wrong.
746     if (!que->size()) {
747         log_error(_("No response from INVOKE of NetConnection connect"));
748     }
749 
750     // There is a queue of queues used to hold all the messages. The first queue
751     // is indexed by the channel number, the second queue is all the messages that
752     // have arrived for that channel.
753     while (que->size()) {	// see if there are any messages at all
754 	log_network(_("%s: There are %d channel queues in the RTMP input queue, %d messages in front queue"),
755 		  __PRETTY_FUNCTION__, que->size(), que->front()->size());
756 	// Get the CQue for the first channel
757 	CQue *channel_q = que->front();
758 	que->pop_front();	// remove this Cque from the top level que
759 
760 	while (channel_q->size()) {
761 	    // Get the first message in the channel queue
762 	    std::shared_ptr<cygnal::Buffer> ptr = channel_q->pop();
763   	    ptr->dump();
764 	    if (ptr) {		// If there is legit data
765 		rthead = decodeHeader(ptr->reference());
766 		if (!rthead) {
767 		    log_error(_("Couldn't decode RTMP message header"));
768 		    continue;
769 		}
770 		switch (rthead->type) {
771 		  case RTMP::NONE:
772 		      log_error(_("RTMP packet can't be of none type!"));
773 
774 		      break;
775 		  case RTMP::CHUNK_SIZE:
776 		      log_unimpl(_("Server message data packet"));
777 
778 		      break;
779 		  case RTMP::ABORT:
780 		      log_unimpl(_("Abort packet"));
781 		      break;
782 		  case RTMP::BYTES_READ:
783 		      log_unimpl(_("Bytes Read data packet"));
784 		      break;
785 		  case RTMP::USER:
786 		  {
787 		      std::shared_ptr<RTMP::rtmp_ping_t> ping = decodePing(ptr->reference() + rthead->head_size);
788 		      log_network(_("Got a Ping type %s"), ping_str[ping->type]);
789 		      break;
790 		  }
791 		  case RTMP::WINDOW_SIZE:
792 		      log_unimpl(_("Set Window Size message data packet"));
793 		      break;
794 		  case RTMP::SET_BANDWITH:
795 		      log_unimpl(_("Set Bandwidthmessage data packet"));
796 		      break;
797 		  case RTMP::ROUTE:
798 		      log_unimpl(_("Route from other server packet"));
799 		      break;
800 		  case RTMP::AUDIO_DATA:
801 		  {
802 		      std::shared_ptr<RTMPMsg> msg = decodeMsgBody(ptr->reference() + rthead->head_size, rthead->bodysize);
803 		      if (msg) {
804 			  msgque.push_back(msg);
805 		      }
806 		      break;
807 		  }
808 		  case RTMP::VIDEO_DATA:
809 		  {
810 		      std::shared_ptr<RTMPMsg> msg = decodeMsgBody(ptr->reference() + rthead->head_size, rthead->bodysize);
811 		      if (msg) {
812 			  msgque.push_back(msg);
813 		      }
814 		      break;
815 		  }
816 		  case RTMP::SHARED_OBJ:
817 		      log_unimpl(_("AMF0 Shared Object data packet message"));
818 		      break;
819 		  case RTMP::AMF3_NOTIFY:
820 		      log_unimpl(_("AMF3 Notify data packet message"));
821 		      break;
822 		  case RTMP::AMF3_SHARED_OBJ:
823 		      log_unimpl(_("AMF3 Shared Object data packet message"));
824 		      break;
825 		  case RTMP::AMF3_INVOKE:
826 		      log_unimpl(_("AMF0 Invoke packet message"));
827 		      break;
828 		  case RTMP::NOTIFY:
829 		      log_unimpl(_("AMF0 Notify data packet message"));
830 		      break;
831 		  case RTMP::INVOKE:
832 		  {
833 		      std::shared_ptr<RTMPMsg> msg = decodeMsgBody(ptr->reference() + rthead->head_size, rthead->bodysize);
834 		      if (msg) {
835 			  msgque.push_back(msg);
836 		      }
837 		      break;
838 		  }
839 		  case RTMP::FLV_DATA:
840 		      log_unimpl(_("FLV data packet message"));
841 		      break;
842 		  default :
843 		      log_error(_("Couldn't decode RTMP message Body"));
844 		      break;
845 		}
846 	    }
847 	}
848     }
849 
850     return msgque;
851 }
852 
853 
854 // bool
855 // RTMPClient::packetRequest()
856 // {
857 //     GNASH_REPORT_FUNCTION;
858 //     return false;
859 // }
860 
861 } // end of gnash namespace
862 
863 // local Variables:
864 // mode: C++
865 // indent-tabs-mode: nil
866 // End:
867