1 // http.cpp:  HyperText Transport Protocol handler for Cygnal, 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 #include <mutex>
22 #include <boost/tokenizer.hpp>
23 #include <boost/date_time/posix_time/posix_time.hpp>
24 #include <boost/date_time/gregorian/gregorian.hpp>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <string>
29 #include <iostream>
30 #include <cstring>
31 #include <sstream>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <algorithm>
35 
36 #include "GnashSystemIOHeaders.h" // read()
37 #include "http.h"
38 #include "amf.h"
39 #include "element.h"
40 #include "cque.h"
41 #include "log.h"
42 #include "network.h"
43 // #include "handler.h"
44 #include "utility.h"
45 #include "buffer.h"
46 #include "diskstream.h"
47 #include "cache.h"
48 
49 // Not POSIX, so best not rely on it if possible.
50 #ifndef PATH_MAX
51 # define PATH_MAX 1024
52 #endif
53 
54 #if defined(_WIN32) || defined(WIN32)
55 # define __PRETTY_FUNCTION__ __FUNCDNAME__
56 # include <winsock2.h>
57 # include <direct.h>
58 #else
59 # include <unistd.h>
60 # include <sys/param.h>
61 #endif
62 
63 using std::string;
64 
65 static std::mutex stl_mutex;
66 
67 namespace gnash
68 {
69 
70 // extern map<int, Handler *> handlers;
71 
72 #if 0
73 // FIXME, this seems too small to me.  --gnu
74 static const int readsize = 1024;
75 #endif
76 
77 static Cache& cache = Cache::getDefaultInstance();
78 
HTTP()79 HTTP::HTTP()
80     : _filetype(DiskStream::FILETYPE_HTML),
81       _filesize(0),
82       _keepalive(false),
83 //       _handler(0),
84       _clientid(0),
85       _index(0),
86       _max_requests(0),
87       _close(false)
88 {
89 //    GNASH_REPORT_FUNCTION;
90 //    struct status_codes *status = new struct status_codes;
91     _version.major = 0;
92     _version.minor = 0;
93 
94 //    _status_codes(CONTINUE, status);
95 }
96 
97 #if 0
98 HTTP::HTTP(Handler *hand)
99     : _filetype(DiskStream::FILETYPE_HTML),
100       _filesize(0),
101       _keepalive(false),
102       _clientid(0),
103       _index(0),
104       _max_requests(0),
105       _close(false)
106 
107 {
108 //    GNASH_REPORT_FUNCTION;
109 //     _handler = hand;
110     _version.major = 0;
111     _version.minor = 0;
112 }
113 #endif
114 
~HTTP()115 HTTP::~HTTP()
116 {
117 //    GNASH_REPORT_FUNCTION;
118 }
119 
120 bool
clearHeader()121 HTTP::clearHeader()
122 {
123 //     _header.str("");
124     _buffer.clear();
125     _filesize = 0;
126     _max_requests = 0;
127 
128     return true;
129 }
130 
131 HTTP &
operator =(HTTP &)132 HTTP::operator = (HTTP& /*obj*/)
133 {
134     GNASH_REPORT_FUNCTION;
135 //    this = obj;
136     // TODO: FIXME !
137     return *this;
138 }
139 
140 
141 std::uint8_t *
processHeaderFields(cygnal::Buffer * buf)142 HTTP::processHeaderFields(cygnal::Buffer *buf)
143 {
144   //    GNASH_REPORT_FUNCTION;
145     string head(reinterpret_cast<const char *>(buf->reference()), buf->size());
146 
147     // The end of the header block is always followed by a blank line
148     string::size_type end = head.find("\r\n\r\n", 0);
149 //    head.erase(end, buf.size()-end);
150     Tok t(head, Sep("\r\n"));
151     for (Tok::iterator i = t.begin(); i != t.end(); ++i) {
152 	string::size_type pos = i->find(":", 0);
153  	if (pos != string::npos) {
154 	    string name = i->substr(0, pos);
155 	    string value = i->substr(pos+2, i->size());
156  	    std::transform(name.begin(), name.end(), name.begin(),
157  			   (int(*)(int)) tolower);
158  	    std::transform(value.begin(), value.end(), value.begin(),
159  			   (int(*)(int)) tolower);
160  	    _fields[name] = value;
161 	    if (name == "keep-alive") {
162 		_keepalive = true;
163 		if ((value != "on") && (value != "off")) {
164 		    _max_requests = strtol(value.c_str(), nullptr, 0);
165 		    // log_debug("Setting Max Requests for Keep-Alive to %d", _max_requests);
166 		}
167 	    }
168 	    if (name == "connection") {
169 		if (value.find("keep-alive", 0) != string::npos) {
170 		    _keepalive = true;
171 		}
172 	    }
173 	    if (name == "content-length") {
174 		_filesize = strtol(value.c_str(), nullptr, 0);
175 		log_debug(_("Setting Content Length to %d"), _filesize);
176 	    }
177 	    if (name == "content-type") {
178 		// This is the type used by flash when sending a AMF data via POST
179 		if (value == "application/x-amf") {
180 //		    log_debug("Got AMF data in the POST request!");
181 		    _filetype = DiskStream::FILETYPE_AMF;
182 		}
183 		// This is the type used by wget when sending a file via POST
184 		if (value == "application/x-www-form-urlencoded") {
185 //		    log_debug("Got file data in the POST request");
186 		    _filetype = DiskStream::FILETYPE_ENCODED;
187 		}
188 		log_debug(_("Setting Content Type to %d"), _filetype);
189 	    }
190 
191 //	    cerr << "FIXME: " << (void *)i << " : " << dec <<  end << endl;
192 	} else {
193 	    const std::uint8_t *cmd = reinterpret_cast<const std::uint8_t *>(i->c_str());
194 	    if (extractCommand(const_cast<std::uint8_t *>(cmd)) == HTTP::HTTP_NONE) {
195 		break;
196 #if 1
197 	    } else {
198 		log_debug(_("Got a request, parsing \"%s\""), *i);
199 		string::size_type start = i->find(" ");
200 		string::size_type params = i->find("?");
201 		string::size_type pos = i->find("HTTP/");
202 		if (pos != string::npos) {
203 		    // The version is the last field and is the protocol name
204 		    // followed by a slash, and the version number. Note that
205 		    // the version is not a double, even though it has a dot
206 		    // in it. It's actually two separate integers.
207 		    _version.major = i->at(pos+5) - '0';
208 		    _version.minor = i->at(pos+7) - '0';
209 		    // log_debug (_("Version: %d.%d"), _version.major, _version.minor);
210 		    // the filespec in the request is the middle field, deliminated
211 		    // by a space on each end.
212 		    if (params != string::npos) {
213 			_params = i->substr(params+1, end);
214 			_filespec = i->substr(start+1, params);
215 			log_debug(_("Parameters for file: \"%s\""), _params);
216 		    } else {
217 			_filespec = i->substr(start+1, pos-start-2);
218 		    }
219 		    log_debug(_("Requesting file: \"%s\""), _filespec);
220 
221 		    // HTTP 1.1 enables persistent network connections
222 		    // by default.
223 		    if (_version.minor > 0) {
224 			log_debug(_("Enabling Keep Alive by default for HTTP > 1.0"));
225 			_keepalive = true;
226 		    }
227 		}
228 	    }
229 #endif
230 	}
231     }
232 
233     return buf->reference() + end + 4;
234 }
235 
236 // // Parse an Echo Request message coming from the Red5 echo_test. This
237 // // method should only be used for testing purposes.
238 // vector<std::shared_ptr<cygnal::Element > >
239 // HTTP::parseEchoRequest(std::uint8_t *data, size_t size)
240 // {
241 // //    GNASH_REPORT_FUNCTION;
242 
243 //     vector<std::shared_ptr<cygnal::Element > > headers;
244 
245 //     // skip past the header bytes, we don't care about them.
246 //     std::uint8_t *tmpptr = data + 6;
247 
248 //     std::uint16_t length;
249 //     length = ntohs((*(std::uint16_t *)tmpptr) & 0xffff);
250 //     tmpptr += sizeof(std::uint16_t);
251 
252 //     // Get the first name, which is a raw string, and not preceded by
253 //     // a type byte.
254 //     std::shared_ptr<cygnal::Element > el1(new cygnal::Element);
255 
256 //     // If the length of the name field is corrupted, then we get out of
257 //     // range quick, and corrupt memory. This is a bit of a hack, but
258 //     // reduces memory errors caused by some of the corrupted tes cases.
259 //     std::uint8_t *endstr = std::find(tmpptr, tmpptr+length, '\0');
260 //     if (endstr != tmpptr+length) {
261 // 	log_debug("Caught corrupted string! length was %d, null at %d",
262 // 		  length,  endstr-tmpptr);
263 // 	length = endstr-tmpptr;
264 //     }
265 //     el1->setName(tmpptr, length);
266 //     tmpptr += length;
267 //     headers.push_back(el1);
268 
269 //     // Get the second name, which is a raw string, and not preceded by
270 //     // a type byte.
271 //     length = ntohs((*(std::uint16_t *)tmpptr) & 0xffff);
272 //     tmpptr += sizeof(std::uint16_t);
273 //     std::shared_ptr<cygnal::Element > el2(new cygnal::Element);
274 
275 // //     std::string name2(reinterpret_cast<const char *>(tmpptr), length);
276 // //     el2->setName(name2.c_str(), name2.size());
277 //     // If the length of the name field is corrupted, then we get out of
278 //     // range quick, and corrupt memory. This is a bit of a hack, but
279 //     // reduces memory errors caused by some of the corrupted tes cases.
280 //     endstr = std::find(tmpptr, tmpptr+length, '\0');
281 //     if (endstr != tmpptr+length) {
282 // 	log_debug("Caught corrupted string! length was %d, null at %d",
283 // 		  length,  endstr-tmpptr);
284 // 	length = endstr-tmpptr;
285 //     }
286 //     el2->setName(tmpptr, length);
287 //     headers.push_back(el2);
288 //     tmpptr += length;
289 
290 //     // Get the last two pieces of data, which are both AMF encoded
291 //     // with a type byte.
292 //     amf::AMF amf;
293 //     std::shared_ptr<cygnal::Element> el3 = amf.extractAMF(tmpptr, tmpptr + size);
294 //     headers.push_back(el3);
295 //     tmpptr += amf.totalsize();
296 
297 //     std::shared_ptr<cygnal::Element> el4 = amf.extractAMF(tmpptr, tmpptr + size);
298 //     headers.push_back(el4);
299 
300 //      return headers;
301 // }
302 
303 // // format a response to the 'echo' test used for testing Gnash. This
304 // // is only used for testing by developers. The format appears to be
305 // // two strings, followed by a double, followed by the "onResult".
306 // cygnal::Buffer &
307 // HTTP::formatEchoResponse(const std::string &num, cygnal::Element &el)
308 // {
309 // //    GNASH_REPORT_FUNCTION;
310 //     std::shared_ptr<cygnal::Buffer> data;
311 
312 //     cygnal::Element nel;
313 //     if (el.getType() == cygnal::Element::TYPED_OBJECT_AMF0) {
314 // 	nel.makeTypedObject();
315 // 	string name = el.getName();
316 // 	nel.setName(name);
317 // 	if (el.propertySize()) {
318 // 	    // FIXME: see about using std::reverse() instead.
319 // 	    for (int i=el.propertySize()-1; i>=0; i--) {
320 // // 	    for (int i=0 ; i<el.propertySize(); i++) {
321 // 		std::shared_ptr<cygnal::Element> child = el.getProperty(i);
322 // 		nel.addProperty(child);
323 // 	    }
324 // 	    data = nel.encode();
325 // 	} else {
326 // 	    data = el.encode();
327 // 	}
328 //     } else {
329 // 	data = el.encode();
330 //     }
331 
332 //     return formatEchoResponse(num, data->reference(), data->allocated());
333 // }
334 
335 #if 0				// FIXME:
336 // Client side parsing of response message codes
337 std::shared_ptr<HTTP::http_response_t>
338 HTTP::parseStatus(const std::string &line)
339 {
340 //    GNASH_REPORT_FUNCTION;
341 
342     std::shared_ptr<http_response_t> status;
343     // The respnse is a number followed by the error message.
344     string::size_type pos = line.find(" ", 0);
345     if (pos != string::npos) {
346 	status.reset(new http_response_t);
347 	status->code = static_cast<HTTP::http_status_e>(strtol(line.substr(0, pos).c_str(), NULL, 0));
348 	status->msg = line.substr(pos+1, line.size());
349     }
350 
351     return status;
352 }
353 
354 HTTP::http_method_e
355 HTTP::processClientRequest(int fd)
356 {
357 //    GNASH_REPORT_FUNCTION;
358     bool result = false;
359 
360     std::shared_ptr<cygnal::Buffer> buf(_que.peek());
361     if (buf) {
362 	_cmd = extractCommand(buf->reference());
363 	switch (_cmd) {
364 	  case HTTP::HTTP_GET:
365 	      result = processGetRequest(fd);
366 	      break;
367 	  case HTTP::HTTP_POST:
368 	      result = processPostRequest(fd);
369 	      break;
370 	  case HTTP::HTTP_HEAD:
371 	      result = processHeadRequest(fd);
372 	      break;
373 	  case HTTP::HTTP_CONNECT:
374 	      result = processConnectRequest(fd);
375 	      break;
376 	  case HTTP::HTTP_TRACE:
377 	      result = processTraceRequest(fd);
378 	      break;
379 	  case HTTP::HTTP_OPTIONS:
380 	      result = processOptionsRequest(fd);
381 	      break;
382 	  case HTTP::HTTP_PUT:
383 	      result = processPutRequest(fd);
384 	      break;
385 	  case HTTP::HTTP_DELETE:
386 	      result = processDeleteRequest(fd);
387 	      break;
388 	  default:
389 	      break;
390 	}
391     }
392 
393     return _cmd;
394 }
395 
396 // A GET request asks the server to send a file to the client
397 bool
398 HTTP::processGetRequest(int fd)
399 {
400     GNASH_REPORT_FUNCTION;
401 
402 //     std::uint8_t buffer[readsize+1];
403 //     const char *ptr = reinterpret_cast<const char *>(buffer);
404 //     memset(buffer, 0, readsize+1);
405 
406 //    _handler->wait();
407 //    _handler->dump();
408 
409     cerr << "QUE = " << _que.size() << endl;
410 
411     if (_que.size() == 0) {
412 	return false;
413     }
414 
415     std::shared_ptr<cygnal::Buffer> buf(_que.pop());
416 //    cerr << "YYYYYYY: " << (char *)buf->reference() << endl;
417 //    cerr << hexify(buf->reference(), buf->allocated(), false) << endl;
418 
419     if (buf == 0) {
420      //	log_debug("Que empty, net connection dropped for fd #%d", getFileFd());
421 	log_debug(_("Que empty, net connection dropped for fd #%d"), fd);
422 	return false;
423     }
424 
425     clearHeader();
426     processHeaderFields(*buf);
427 
428     string url = _docroot + _filespec;
429     // See if the file is in the cache and already opened.
430     std::shared_ptr<DiskStream> filestream(cache.findFile(url));
431     if (filestream) {
432 	log_network(_("FIXME: found file in cache!"));
433     } else {
434 	filestream.reset(new DiskStream);
435 //	    cerr << "New Filestream at 0x" << hex << filestream.get() << endl;
436 
437 //	    cache.addFile(url, filestream);	FIXME: always reload from disk for now.
438 
439 	// Oopen the file and read the first chunk into memory
440 	if (filestream->open(url)) {
441 	    formatErrorResponse(HTTP::NOT_FOUND);
442 	} else {
443 	    // Get the file size for the HTTP header
444 	    if (filestream->getFileType() == DiskStream::FILETYPE_NONE) {
445 		formatErrorResponse(HTTP::NOT_FOUND);
446 	    } else {
447 		cache.addPath(_filespec, filestream->getFilespec());
448 	    }
449 	}
450     }
451 
452     // Send the reply
453     cygnal::Buffer &reply = formatHeader(filestream->getFileType(),
454 					  filestream->getFileSize(),
455 					  HTTP::OK);
456     writeNet(fd, reply);
457 
458     size_t filesize = filestream->getFileSize();
459     size_t bytes_read = 0;
460     int ret;
461     size_t page = 0;
462     if (filesize) {
463 #ifdef USE_STATS_CACHE
464 	struct timespec start;
465 	clock_gettime (CLOCK_REALTIME, &start);
466 #endif
467 	size_t getbytes = 0;
468 	if (filesize <= filestream->getPagesize()) {
469 	    getbytes = filesize;
470 	} else {
471 	    getbytes = filestream->getPagesize();
472 	}
473 	if (filesize >= CACHE_LIMIT) {
474 	    do {
475 		filestream->loadToMem(page);
476 		ret = writeNet(fd, filestream->get(), getbytes);
477 		if (ret <= 0) {
478 		    break;
479 		}
480 		bytes_read += ret;
481 		page += filestream->getPagesize();
482 	    } while (bytes_read <= filesize);
483 	} else {
484 	    filestream->loadToMem(filesize, 0);
485 	    ret = writeNet(fd, filestream->get(), filesize);
486 	}
487 	filestream->close();
488 #ifdef USE_STATS_CACHE
489 	struct timespec end;
490 	clock_gettime (CLOCK_REALTIME, &end);
491 	double time = (end.tv_sec - start.tv_sec) + ((end.tv_nsec - start.tv_nsec)/1e9);
492 	cerr << "File " << _filespec
493 	     << " transferred " << filesize << " bytes in: " << fixed
494 	     << time << " seconds for net fd #" << fd << endl;
495 #endif
496     }
497 
498     log_debug(_("http_handler all done transferring requested file \"%s\"."),
499 	      _filespec);
500 
501     return true;
502 }
503 
504 // A POST request asks sends a data from the client to the server. After processing
505 // the header like we normally do, we then read the amount of bytes specified by
506 // the "content-length" field, and then write that data to disk, or decode the amf.
507 bool
508 HTTP::processPostRequest(int fd)
509 {
510     GNASH_REPORT_FUNCTION;
511 
512 //    cerr << "QUE1 = " << _que.size() << endl;
513 
514     if (_que.size() == 0) {
515 	return false;
516     }
517 
518     std::shared_ptr<cygnal::Buffer> buf(_que.pop());
519     if (buf == 0) {
520 	log_debug(_("Que empty, net connection dropped for fd #%d"), getFileFd());
521 	return false;
522     }
523 //    cerr << __FUNCTION__ << buf->allocated() << " : " << hexify(buf->reference(), buf->allocated(), true) << endl;
524 
525     clearHeader();
526     std::uint8_t *data = processHeaderFields(*buf);
527     size_t length = strtol(getField("content-length").c_str(), NULL, 0);
528     std::shared_ptr<cygnal::Buffer> content(new cygnal::Buffer(length));
529     int ret = 0;
530     if (buf->allocated() - (data - buf->reference()) ) {
531 //	cerr << "Don't need to read more data: have " << buf->allocated() << " bytes" << endl;
532 	content->copy(data, length);
533 	ret = length;
534     } else {
535 //	cerr << "Need to read more data, only have "  << buf->allocated() << " bytes" << endl;
536 	ret = readNet(fd, *content, 2);
537 	data = content->reference();
538     }
539 
540     if (getField("content-type") == "application/x-www-form-urlencoded") {
541 	log_debug(_("Got file data in POST"));
542 	string url = _docroot + _filespec;
543 	DiskStream ds(url, *content);
544 	ds.writeToDisk();
545 //    ds.close();
546 	// oh boy, we got ourselves some encoded AMF objects instead of a boring file.
547     } else if (getField("content-type") == "application/x-amf") {
548 	log_debug(_("Got AMF data in POST"));
549 #if 0
550 	amf::AMF amf;
551 	std::shared_ptr<cygnal::Element> el = amf.extractAMF(content.reference(), content.end());
552 	el->dump();		// FIXME: do something intelligent
553 				// with this Element
554 #endif
555     }
556 
557     // Send the reply
558 
559     // NOTE: this is a "special" path we trap until we have real CGI support
560     if ((_filespec == "/echo/gateway")
561 	&& (getField("content-type") == "application/x-amf")) {
562 //	const char *num = (const char *)buf->at(10);
563 	log_debug(_("Got CGI echo request in POST"));
564 //	cerr << "FIXME 2: " << hexify(content->reference(), content->allocated(), true) << endl;
565 
566 	vector<std::shared_ptr<cygnal::Element> > headers = parseEchoRequest(*content);
567   	//std::shared_ptr<cygnal::Element> &el0 = headers[0];
568   	//std::shared_ptr<cygnal::Element> &el1 = headers[1];
569   	//std::shared_ptr<cygnal::Element> &el3 = headers[3];
570 
571     if (headers.size() >= 4) {
572 	    if (headers[3]) {
573 		cygnal::Buffer &reply = formatEchoResponse(headers[1]->getName(), *headers[3]);
574 // 	    cerr << "FIXME 3: " << hexify(reply.reference(), reply.allocated(), true) << endl;
575 // 	    cerr << "FIXME 3: " << hexify(reply.reference(), reply.allocated(), false) << endl;
576 		writeNet(fd, reply);
577 	    }
578  	}
579     } else {
580 	cygnal::Buffer &reply = formatHeader(_filetype, _filesize, HTTP::OK);
581 	writeNet(fd, reply);
582     }
583 
584     return true;
585 }
586 
587 bool
588 HTTP::processPutRequest(int /* fd */)
589 {
590 //    GNASH_REPORT_FUNCTION;
591     log_unimpl(_("PUT request"));
592 
593     return false;
594 }
595 
596 bool
597 HTTP::processDeleteRequest(int /* fd */)
598 {
599 //    GNASH_REPORT_FUNCTION;
600     log_unimpl(_("DELETE request"));
601     return false;
602 }
603 
604 bool
605 HTTP::processConnectRequest(int /* fd */)
606 {
607 //    GNASH_REPORT_FUNCTION;
608     log_unimpl(_("CONNECT request"));
609     return false;
610 }
611 
612 bool
613 HTTP::processOptionsRequest(int /* fd */)
614 {
615 //    GNASH_REPORT_FUNCTION;
616     log_unimpl(_("OPTIONS request"));
617     return false;
618 }
619 
620 bool
621 HTTP::processHeadRequest(int /* fd */)
622 {
623 //    GNASH_REPORT_FUNCTION;
624     log_unimpl(_("HEAD request"));
625     return false;
626 }
627 
628 bool
629 HTTP::processTraceRequest(int /* fd */)
630 {
631 //    GNASH_REPORT_FUNCTION;
632     log_unimpl(_("TRACE request"));
633     return false;
634 }
635 #endif
636 
637 // Convert the Content-Length field to a number we can use
638 size_t
getContentLength()639 HTTP::getContentLength()
640 {
641     //    GNASH_REPORT_FUNCTION;
642     std::string length = getField("content-length");
643     if (length.size() > 0) {
644 	return static_cast<size_t>(strtol(length.c_str(), nullptr, 0));
645     }
646 
647     return 0;
648 }
649 
650 
651 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5 (5.3 Request Header Fields)
652 bool
checkRequestFields(cygnal::Buffer &)653 HTTP::checkRequestFields(cygnal::Buffer & /* buf */)
654 {
655 //    GNASH_REPORT_FUNCTION;
656 
657 #if 0
658     const char *foo[] = {
659 	"Accept",
660 	"Accept-Charset",
661 	"Accept-Encoding",
662 	"Accept-Language",
663 	"Authorization",
664 	"Expect",
665 	"From",
666 	"Host",
667 	"If-Match",
668 	"If-Modified-Since",
669 	"If-None-Match",
670 	"If-Range",
671 	"If-Unmodified-Since",
672 	"Max-Forwards",
673 	"Proxy-Authorization",
674 	"Range",
675 	"Referer",
676 	"TE",
677 	"User-Agent",
678 	0
679 	};
680 #endif
681     return false;
682 }
683 
684 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec7 (7.1 Entity Header Fields)
685 bool
checkEntityFields(cygnal::Buffer &)686 HTTP::checkEntityFields(cygnal::Buffer & /* buf */)
687 {
688 //    GNASH_REPORT_FUNCTION;
689 
690 #if 0
691     const char *foo[] = {
692 	"Accept",
693 	"Allow",
694 	"Content-Encoding",
695 	"Content-Language",
696 	"Content-Length",	// Must be used when sending a Response
697 	"Content-Location",
698 	"Content-MD5",
699 	"Content-Range",
700 	"Content-Type",		// Must be used when sending non text/html files
701 	"Expires",
702 	"Last-Modified",
703 	0
704 	};
705 
706     return true;
707 #endif
708     return false;
709 }
710 
711 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec4 (4.5 General Header Fields)
712 bool
checkGeneralFields(cygnal::Buffer &)713 HTTP::checkGeneralFields(cygnal::Buffer & /* buf */)
714 {
715 //    GNASH_REPORT_FUNCTION;
716 
717 #if 0
718     const char *foo[] = {
719 	"Cache-Control"
720 	"Connection",		// Must look for Keep-Alive and close
721 	"Date",
722 	"Pragma",
723 	"Trailer",
724 	"Transfer-Encoding",	// Must look for Chunked-Body too
725 	"Upgrade",
726 	"Via",
727 	"Warning",
728 	0
729 	};
730 #endif
731     return false;
732 }
733 
734 std::shared_ptr<std::vector<std::string> >
getFieldItem(const std::string & name)735 HTTP::getFieldItem(const std::string &name)
736 {
737 //    GNASH_REPORT_FUNCTION;
738     std::shared_ptr<std::vector<std::string> > ptr(new std::vector<std::string>);
739     Tok t(_fields[name], Sep(", "));
740     for (Tok::iterator i = t.begin(), e = t.end(); i != e; ++i) {
741 	ptr->push_back(*i);
742     }
743 
744     return ptr;
745 }
746 
747 bool
startHeader()748 HTTP::startHeader()
749 {
750 //    GNASH_REPORT_FUNCTION;
751 
752     clearHeader();
753 
754     return true;
755 }
756 
757 cygnal::Buffer &
formatHeader(http_status_e type)758 HTTP::formatHeader(http_status_e type)
759 {
760 //    GNASH_REPORT_FUNCTION;
761 
762     return formatHeader(_filesize, type);
763 }
764 
765 cygnal::Buffer &
formatCommon(const string & data)766 HTTP::formatCommon(const string &data)
767 {
768 //    GNASH_REPORT_FUNCTION;
769     _buffer += data;
770     _buffer += "\r\n";
771 
772     return _buffer;
773 }
774 
775 cygnal::Buffer &
formatHeader(size_t size,http_status_e code)776 HTTP::formatHeader(size_t size, http_status_e code)
777 {
778 //    GNASH_REPORT_FUNCTION;
779   return formatHeader(_filetype, size, code);
780 }
781 
782 cygnal::Buffer &
formatHeader(DiskStream::filetype_e type,size_t size,http_status_e code)783 HTTP::formatHeader(DiskStream::filetype_e type, size_t size, http_status_e code)
784 {
785 //    GNASH_REPORT_FUNCTION;
786 
787     clearHeader();
788 
789     char num[12];
790 
791     _buffer = "HTTP/";
792     sprintf(num, "%d.%d", _version.major, _version.minor);
793     _buffer += num;
794     sprintf(num, " %d ", static_cast<int>(code));
795     _buffer += num;
796     switch (code) {
797       case CONTINUE:
798 	  _buffer += "Continue";
799 	  break;
800       case SWITCHPROTOCOLS:
801 	  _buffer += "Switch Protocols";
802 	  break;
803 	  // 2xx: Success - The action was successfully received,
804 	  // understood, and accepted
805 	  break;
806       case OK:
807 	  _buffer += "OK";
808 	  break;
809       case CREATED:
810 	  _buffer += "Created";
811 	  break;
812       case ACCEPTED:
813 	  _buffer += "Accepted";
814 	  break;
815       case NON_AUTHORITATIVE:
816 	  _buffer += "Non Authoritive";
817 	  break;
818       case NO_CONTENT:
819 	  _buffer += "No Content";
820 	  break;
821       case RESET_CONTENT:
822 	  _buffer += "Reset Content";
823 	  break;
824       case PARTIAL_CONTENT:
825 	  _buffer += "Partial Content";
826 	  break;
827         // 3xx: Redirection - Further action must be taken in order to
828         // complete the request
829       case MULTIPLE_CHOICES:
830 	  _buffer += "Multiple Choices";
831 	  break;
832       case MOVED_PERMANENTLY:
833 	  _buffer += "Moved Permanently";
834 	  break;
835       case FOUND:
836 	  _buffer += "Found";
837 	  break;
838       case SEE_OTHER:
839 	  _buffer += "See Other";
840 	  break;
841       case NOT_MODIFIED:
842 	  _buffer += "Not Modified";
843 	  break;
844       case USE_PROXY:
845 	  _buffer += "Use Proxy";
846 	  break;
847       case TEMPORARY_REDIRECT:
848 	  _buffer += "Temporary Redirect";
849 	  break;
850         // 4xx: Client Error - The request contains bad syntax or
851         // cannot be fulfilled
852       case BAD_REQUEST:
853 	  _buffer += "Bad Request";
854 	  break;
855       case UNAUTHORIZED:
856 	  _buffer += "Unauthorized";
857 	  break;
858       case PAYMENT_REQUIRED:
859 	  _buffer += "Payment Required";
860 	  break;
861       case FORBIDDEN:
862 	  _buffer += "Forbidden";
863 	  break;
864       case NOT_FOUND:
865 	  _buffer += "Not Found";
866 	  break;
867       case METHOD_NOT_ALLOWED:
868 	  _buffer += "Method Not Allowed";
869 	  break;
870       case NOT_ACCEPTABLE:
871 	  _buffer += "Not Acceptable";
872 	  break;
873       case PROXY_AUTHENTICATION_REQUIRED:
874 	  _buffer += "Proxy Authentication Required";
875 	  break;
876       case REQUEST_TIMEOUT:
877 	  _buffer += "Request Timeout";
878 	  break;
879       case CONFLICT:
880 	  _buffer += "Conflict";
881 	  break;
882       case GONE:
883 	  _buffer += "Gone";
884 	  break;
885       case LENGTH_REQUIRED:
886 	  _buffer += "Length Required";
887 	  break;
888       case PRECONDITION_FAILED:
889 	  _buffer += "Precondition Failed";
890 	  break;
891       case REQUEST_ENTITY_TOO_LARGE:
892 	  _buffer += "Request Entity Too Large";
893 	  break;
894       case REQUEST_URI_TOO_LARGE:
895 	  _buffer += "Request URI Too Large";
896 	  break;
897       case UNSUPPORTED_MEDIA_TYPE:
898 	  _buffer += "Unsupported Media Type";
899 	  break;
900       case REQUESTED_RANGE_NOT_SATISFIABLE:
901 	  _buffer += "Request Range Not Satisfiable";
902 	  break;
903       case EXPECTATION_FAILED:
904 	  _buffer += "Expectation Failed";
905 	  break;
906 	  // 5xx: Server Error - The server failed to fulfill an apparently valid request
907       case INTERNAL_SERVER_ERROR:
908 	  _buffer += "Internal Server Error";
909 	  break;
910       case NOT_IMPLEMENTED:
911 	  _buffer += "Method Not Implemented";
912 	  break;
913       case BAD_GATEWAY:
914 	  _buffer += "Bad Gateway";
915 	  break;
916       case SERVICE_UNAVAILABLE:
917 	  _buffer += "Service Unavailable";
918 	  break;
919       case GATEWAY_TIMEOUT:
920 	  _buffer += "Gateway Timeout";
921 	  break;
922       case HTTP_VERSION_NOT_SUPPORTED:
923 	  _buffer += "HTTP Version Not Supported";
924 	  break;
925 	  // Gnash/Cygnal extensions for internal use
926       case LIFE_IS_GOOD:
927 	  break;
928       case CLOSEPIPE:
929 	  _buffer += "Close Pipe";
930 	  break;
931       default:
932 	  break;
933     }
934 
935     // end the line
936     _buffer += "\r\n";
937 
938     formatDate();
939     formatServer();
940     formatLastModified();
941     formatAcceptRanges("bytes");
942     formatContentLength(size);
943 
944     // Apache closes the connection on GET requests, so we do the same.
945     // This is a bit silly, because if we close after every GET request,
946     // we're not really handling the persistance of HTTP 1.1 at all.
947     if (_close) {
948 	formatConnection("close");
949 	_keepalive = false;
950     }
951     formatContentType(type);
952 
953     // All HTTP messages are followed by a blank line.
954     terminateHeader();
955 
956     return _buffer;
957 }
958 
959 cygnal::Buffer &
formatDate()960 HTTP::formatDate()
961 {
962 //    GNASH_REPORT_FUNCTION;
963     boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
964 
965 //    cout <<  now.time_of_day() << "\r\n";
966 
967     boost::gregorian::date d(now.date());
968 
969     char num[12];
970 
971     boost::gregorian::greg_weekday wd = d.day_of_week();
972     _buffer += "Date: ";
973     _buffer += wd.as_long_string();
974 
975     _buffer += ", ";
976     sprintf(num, "%d", static_cast<int>(d.day()));
977     _buffer += num;
978 
979     _buffer += " ";
980     _buffer += boost::gregorian::greg_month(d.month()).as_short_string();
981 
982     _buffer += " ";
983     sprintf(num, "%d", static_cast<int>(d.year()));
984     _buffer += num;
985 
986     _buffer += " ";
987     _buffer += boost::posix_time::to_simple_string(now.time_of_day());
988 
989     _buffer += " GMT\r\n";
990 
991     return _buffer;
992 }
993 
994 cygnal::Buffer &
formatServer()995 HTTP::formatServer()
996 {
997 //    GNASH_REPORT_FUNCTION;
998     _buffer += "Server: Cygnal (GNU/Linux)\r\n";
999 
1000     return _buffer;
1001 }
1002 
1003 cygnal::Buffer &
formatServer(const string & data)1004 HTTP::formatServer(const string &data)
1005 {
1006 //    GNASH_REPORT_FUNCTION;
1007     _buffer += "Server: ";
1008     _buffer += data;
1009     _buffer += "\r\n";
1010 
1011     return _buffer;
1012 }
1013 
1014 cygnal::Buffer &
formatContentLength()1015 HTTP::formatContentLength()
1016 {
1017 //    GNASH_REPORT_FUNCTION;
1018 
1019     return formatContentLength(_filesize);
1020 }
1021 
1022 cygnal::Buffer &
formatContentLength(std::uint32_t filesize)1023 HTTP::formatContentLength(std::uint32_t filesize)
1024 {
1025 //    GNASH_REPORT_FUNCTION;
1026 //    _header << "Content-Length: " << filesize << "\r\n";
1027 
1028     _buffer += "Content-Length: ";
1029     char num[12];
1030     sprintf(num, "%d", filesize);
1031     _buffer += num;
1032     _buffer += "\r\n";
1033 
1034     return _buffer;
1035 }
1036 
1037 cygnal::Buffer &
formatContentType()1038 HTTP::formatContentType()
1039 {
1040 //    GNASH_REPORT_FUNCTION;
1041     return formatContentType(_filetype);
1042 }
1043 
1044 cygnal::Buffer &
formatContentType(DiskStream::filetype_e filetype)1045 HTTP::formatContentType(DiskStream::filetype_e filetype)
1046 {
1047 //    GNASH_REPORT_FUNCTION;
1048 
1049     switch (filetype) {
1050       // default to HTML if the type isn't known
1051       case DiskStream::FILETYPE_NONE:
1052 	  _buffer += "Content-Type: text/html\r\n";
1053 	  break;
1054       case DiskStream::FILETYPE_AMF:
1055 	  _buffer += "Content-Type: application/x-amf\r\n";
1056 	  break;
1057       case DiskStream::FILETYPE_SWF:
1058 	  _buffer += "Content-Type: application/x-shockwave-flash\r\n";
1059 	  break;
1060       case DiskStream::FILETYPE_HTML:
1061 	  _buffer += "Content-Type: text/html\r\n";
1062 	  break;
1063     case DiskStream::FILETYPE_PNG:
1064 	  _buffer += "Content-Type: image/png\r\n";
1065 	  break;
1066     case DiskStream::FILETYPE_JPEG:
1067 	  _buffer += "Content-Type: image/jpeg\r\n";
1068 	  break;
1069     case DiskStream::FILETYPE_GIF:
1070 	  _buffer += "Content-Type: image/gif\r\n";
1071 	  break;
1072     case DiskStream::FILETYPE_MP3:
1073 	  _buffer += "Content-Type: audio/mpeg\r\n";
1074 	  break;
1075     case DiskStream::FILETYPE_MP4:
1076 	  _buffer += "Content-Type: video/mp4\r\n";
1077 	  break;
1078     case DiskStream::FILETYPE_OGG:
1079 	  _buffer += "Content-Type: audio/ogg\r\n";
1080 	  break;
1081     case DiskStream::FILETYPE_VORBIS:
1082 	  _buffer += "Content-Type: audio/ogg\r\n";
1083 	  break;
1084     case DiskStream::FILETYPE_THEORA:
1085 	  _buffer += "Content-Type: video/ogg\r\n";
1086 	  break;
1087     case DiskStream::FILETYPE_DIRAC:
1088 	  _buffer += "Content-Type: video/dirac\r\n";
1089 	  break;
1090     case DiskStream::FILETYPE_TEXT:
1091 	  _buffer += "Content-Type: text/plain\r\n";
1092 	  break;
1093     case DiskStream::FILETYPE_FLV:
1094 	  _buffer += "Content-Type: video/x-flv\r\n";
1095 	  break;
1096     case DiskStream::FILETYPE_VP6:
1097 	  _buffer += "Content-Type: video/vp6\r\n";
1098 	  break;
1099     case DiskStream::FILETYPE_XML:
1100 	  _buffer += "Content-Type: application/xml\r\n";
1101 	  break;
1102     case DiskStream::FILETYPE_FLAC:
1103 	  _buffer += "Content-Type: audio/flac\r\n";
1104 	  break;
1105     case DiskStream::FILETYPE_PHP:
1106 	  _buffer += "Content-Type: application/x-httpd-php\r\n";
1107 	  break;
1108       default:
1109 	  _buffer += "Content-Type: text/html\r\n";
1110     }
1111 
1112     return _buffer;
1113 }
1114 
1115 cygnal::Buffer &
formatLastModified()1116 HTTP::formatLastModified()
1117 {
1118 //    GNASH_REPORT_FUNCTION;
1119     boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
1120     std::stringstream date;
1121 
1122     boost::gregorian::date d(now.date());
1123 
1124     date << d.day_of_week();
1125     date << ", " << d.day();
1126     date << " "  << d.month();
1127     date << " "  << d.year();
1128     date << " "  << now.time_of_day();
1129     date << " GMT";
1130 
1131     return formatLastModified(date.str());
1132 }
1133 
1134 cygnal::Buffer &
formatEchoResponse(const std::string & num,cygnal::Buffer & data)1135 HTTP::formatEchoResponse(const std::string &num, cygnal::Buffer &data)
1136 {
1137 //    GNASH_REPORT_FUNCTION;
1138     return formatEchoResponse(num, data.reference(), data.allocated());
1139 }
1140 
1141 cygnal::Buffer &
formatEchoResponse(const std::string & num,std::uint8_t * data,size_t size)1142 HTTP::formatEchoResponse(const std::string &num, std::uint8_t *data, size_t size)
1143 {
1144 //    GNASH_REPORT_FUNCTION;
1145 
1146     //std::uint8_t *tmpptr  = data;
1147 
1148     // FIXME: temporary hacks while debugging
1149     cygnal::Buffer fixme("00 00 00 00 00 01");
1150     cygnal::Buffer fixme2("ff ff ff ff");
1151 
1152     _buffer = "HTTP/1.1 200 OK\r\n";
1153     formatContentType(DiskStream::FILETYPE_AMF);
1154 //    formatContentLength(size);
1155     // FIXME: this is a hack ! Calculate a real size!
1156     formatContentLength(size+29);
1157 
1158     // Don't pretend to be the Red5 server
1159     formatServer("Cygnal (0.8.6)");
1160 
1161     // All HTTP messages are followed by a blank line.
1162     terminateHeader();
1163 
1164     // Add the binary blob for the header
1165     _buffer += fixme;
1166 
1167     // Make the result response, which is the 2nd data item passed in
1168     // the request, a slash followed by a number like "/2".
1169     string result = num;
1170     result += "/onResult";
1171     std::shared_ptr<cygnal::Buffer> res = cygnal::AMF::encodeString(result);
1172     _buffer.append(res->begin()+1, res->size()-1);
1173 
1174     // Add the null data item
1175     std::shared_ptr<cygnal::Buffer> null = cygnal::AMF::encodeString("null");
1176     _buffer.append(null->begin()+1, null->size()-1);
1177 
1178     // Add the other binary blob
1179     _buffer += fixme2;
1180 
1181     cygnal::Element::amf0_type_e type = static_cast<cygnal::Element::amf0_type_e>(*data);
1182     if ((type == cygnal::Element::UNSUPPORTED_AMF0)
1183 	|| (type == cygnal::Element::NULL_AMF0)) {
1184 	_buffer += type;
1185 	// Red5 returns a NULL object when it's recieved an undefined one in the echo_test
1186     } else if (type == cygnal::Element::UNDEFINED_AMF0) {
1187 	_buffer += cygnal::Element::NULL_AMF0;
1188     } else {
1189 	// Add the AMF data we're echoing back
1190 	if (size) {
1191 	    _buffer.append(data, size);
1192 	}
1193     }
1194 
1195     return _buffer;
1196 }
1197 
1198 cygnal::Buffer &
formatRequest(const string & url,http_method_e cmd)1199 HTTP::formatRequest(const string &url, http_method_e cmd)
1200 {
1201 //    GNASH_REPORT_FUNCTION;
1202 
1203     clearHeader();
1204 
1205     switch (cmd) {
1206     case HTTP::HTTP_GET:
1207 	_buffer = "GET ";
1208 	break;
1209     case HTTP::HTTP_POST:
1210 	_buffer = "POST ";
1211 	break;
1212     case HTTP::HTTP_HEAD:
1213 	_buffer = "HEAD ";
1214 	break;
1215     case HTTP::HTTP_CONNECT:
1216 	_buffer = "CONNECT ";
1217 	break;
1218     case HTTP::HTTP_TRACE:
1219 	_buffer = "TRACE ";
1220 	break;
1221     case HTTP::HTTP_OPTIONS:
1222 	_buffer = "OPTIONS ";
1223 	break;
1224     default:
1225 	break;
1226     }
1227     _buffer += url;
1228 
1229     _buffer += " HTTP/1.1";
1230 //     sprintf(num, "%d.%d", _version.major, _version.minor);
1231 //     _buffer += num;
1232     // end the line
1233     _buffer += "\r\n";
1234 
1235     formatHost("localhost");
1236     formatAgent("Gnash");
1237 
1238     // Post messages want a bunch more fields than a GET request
1239     if (cmd == HTTP::HTTP_POST) {
1240 	formatContentType(DiskStream::FILETYPE_AMF);
1241 	formatEncoding("deflate, gzip, x-gzip, identity, *;q=0");
1242 	formatConnection("Keep-Alive");
1243     }
1244 
1245 //     // All HTTP messages are followed by a blank line.
1246 //     terminateHeader();
1247 
1248     return _buffer;
1249 }
1250 
1251 
1252 HTTP::http_method_e
extractCommand(std::uint8_t * data)1253 HTTP::extractCommand(std::uint8_t *data)
1254 {
1255     // GNASH_REPORT_FUNCTION;
1256 
1257 //    string body = reinterpret_cast<const char *>(data);
1258     HTTP::http_method_e cmd = HTTP::HTTP_NONE;
1259 
1260     // force the case to make comparisons easier
1261 //     std::transform(body.begin(), body.end(), body.begin(),
1262 //                (int(*)(int)) toupper);
1263 
1264     // Extract the command
1265     if (memcmp(data, "GET", 3) == 0) {
1266         cmd = HTTP::HTTP_GET;
1267     } else if (memcmp(data, "POST", 4) == 0) {
1268         cmd = HTTP::HTTP_POST;
1269     } else if (memcmp(data, "HEAD", 4) == 0) {
1270         cmd = HTTP::HTTP_HEAD;
1271     } else if (memcmp(data, "CONNECT", 7) == 0) {
1272         cmd = HTTP::HTTP_CONNECT;
1273     } else if (memcmp(data, "TRACE", 5) == 0) {
1274         cmd = HTTP::HTTP_TRACE;
1275     } else if (memcmp(data, "PUT", 3) == 0) {
1276         cmd = HTTP::HTTP_PUT;
1277     } else if (memcmp(data, "OPTIONS", 4) == 0) {
1278         cmd = HTTP::HTTP_OPTIONS;
1279     } else if (memcmp(data, "DELETE", 4) == 0) {
1280         cmd = HTTP::HTTP_DELETE;
1281     } else if (memcmp(data, "HTTP", 4) == 0) {
1282         cmd = HTTP::HTTP_RESPONSE;
1283     }
1284 
1285     // For valid requests, the second argument, delimited by spaces
1286     // is the filespec of the file being requested or transmitted.
1287     if (cmd != HTTP::HTTP_NONE) {
1288 	std::uint8_t *start = std::find(data, data+7, ' ') + 1;
1289 	std::uint8_t *end   = std::find(start + 2, data+PATH_MAX, ' ');
1290 	std::uint8_t *params = std::find(start, end, '?');
1291 	if (params != end) {
1292 	    _params = std::string(params+1, end);
1293 	    _filespec = std::string(start, params);
1294 	    log_debug(_("Parameters for file: \"%s\""), _params);
1295 	} else {
1296 	    // This is fine as long as end is within the buffer.
1297 	    _filespec = std::string(start, end);
1298 	}
1299 	// log_debug("Requesting file: \"%s\"", _filespec);
1300 
1301 	// The third field is always the HTTP version
1302 	// The version is the last field and is the protocol name
1303 	// followed by a slash, and the version number. Note that
1304 	// the version is not a double, even though it has a dot
1305 	// in it. It's actually two separate integers.
1306 	_version.major = *(end+6) - '0';
1307 	_version.minor = *(end+8) - '0';
1308 	// log_debug (_("Version: %d.%d"), _version.major, _version.minor);
1309     }
1310 
1311     return cmd;
1312 }
1313 
1314 /// \brief Send a message to the other end of the network connection.
1315 ///`	Sends the contents of the _header and _body private data to
1316 ///	the already opened network connection.
1317 ///
1318 /// @return The number of bytes sent
1319 int DSOEXPORT
sendMsg()1320 HTTP::sendMsg()
1321 {
1322     GNASH_REPORT_FUNCTION;
1323 
1324     return 0; // FIXME
1325 }
1326 
1327 /// \brief Send a message to the other end of the network connection.
1328 ///`	Sends the contents of the _header and _body private data to
1329 ///	the already opened network connection.
1330 ///
1331 /// @param fd The file descriptor to use for writing to the network.
1332 ///
1333 /// @return The number of bytes sent
1334 int DSOEXPORT
sendMsg(int)1335 HTTP::sendMsg(int /* fd */)
1336 {
1337     GNASH_REPORT_FUNCTION;
1338 
1339     return 0; // FIXME
1340 }
1341 
1342 /// \brief Send a message to the other end of the network connection.
1343 ///`	Sends the contents of the _header and _body private data to
1344 ///	the already opened network connection.
1345 ///
1346 /// @param data A real pointer to the data.
1347 /// @param size The number of bytes of data stored.
1348 ///
1349 /// @return The number of bytes sent
1350 int DSOEXPORT
sendMsg(const std::uint8_t * data,size_t size)1351 HTTP::sendMsg(const std::uint8_t *data, size_t size)
1352 {
1353     GNASH_REPORT_FUNCTION;
1354 //    _header
1355 
1356     return Network::writeNet(data, size);
1357 }
1358 
1359 size_t
recvChunked(std::uint8_t * data,size_t size)1360 HTTP::recvChunked(std::uint8_t *data, size_t size)
1361 {
1362 //     GNASH_REPORT_FUNCTION;
1363     bool done = false;
1364     bool chunks = true;
1365     size_t total = 0;
1366     size_t pktsize = 0;
1367 
1368     if (size == 0) {
1369 	return 0;
1370     }
1371 
1372     // A chunked transfer sends a count of bytes in ASCII hex first,
1373     // and that line is terminated with the usual \r\n HTTP header field
1374     // line number. There is supposed to be a ';' before the \r\n, as this
1375     // field can have other attributes, but the OpenStreetMap server doesn't
1376     // use the semi-colon, as it's optional, and rarely used anyway.
1377     std::shared_ptr<cygnal::Buffer> buf;
1378     std::uint8_t *start = std::find(data, data+size, '\r') + 2;
1379     if (start != data+size) {
1380 	// extract the total size of the chunk
1381 	std::string bytes(data, start-2);
1382 	size_t sizesize = start-data;
1383 	total = static_cast<size_t>(strtol(bytes.c_str(), nullptr, 16));
1384 	log_debug(_("%s: Total size for first chunk is: %d, data size %d (%d)"),
1385 		  __PRETTY_FUNCTION__, total, size, sizesize);
1386 	buf.reset(new cygnal::Buffer(total+2));
1387 	// Add the existing data from the previous packet
1388 	buf->copy(data+sizesize, size-sizesize);
1389     }
1390 
1391     // The size of the packet we need to read has a 2 byte terminator "\r\n",
1392     // like any other HTTP header field, so we have to read those bytes too
1393     // so we can stay sychronized with the start of each chunk.
1394     pktsize = total - buf->allocated() + 2;
1395 //     log_debug("%s: Total Packet size for first chunk is: %d", __PRETTY_FUNCTION__,
1396 // 	      pktsize);
1397 
1398     done = false;
1399     size_t ret = 0;
1400 
1401     // Keep reading chunks as long as they arrive
1402     while (chunks) {
1403 	do {
1404 	    if (!pktsize) {
1405 		total = 0;
1406 		// Only read a few bytes, as we have todo a resize, so the less
1407 		// data to copy the better. We only do this to get the total size
1408 		// of the chunk, which we need to know when it's done. As we can't
1409 		// tell we're done processing chunks till one has a length of
1410 		// "0\r\n", this is important.
1411 		pktsize = 12;
1412 		buf.reset(new cygnal::Buffer(pktsize+2));
1413 	    }
1414 	    ret = readNet(buf->reference() + buf->allocated(), pktsize, 60);
1415  	    //buf->dump();
1416 	    // We got data.
1417 	    if (ret == 0) {
1418 		log_debug(_("no data yet for fd #%d, continuing..."),
1419 			  getFileFd());
1420 		done = true;
1421 	    }
1422 	    if (ret) {
1423 		// manually set the seek pointer in the buffer, as we read
1424 		// the data into the raw memory allocated to the buffer. We
1425 		// only want to do this if we got data of course.
1426 		buf->setSeekPointer(buf->end() + ret);
1427 		if (!total) {
1428 		    start = std::find(buf->reference(), buf->reference()+ret, '\r') + 2;
1429 		    if (start != buf->reference()+ret) {
1430 			// extract the total size of the chunk
1431 			std::string bytes(buf->reference(), start-2);
1432 			total = static_cast<size_t>(strtol(bytes.c_str(), nullptr, 16));
1433 			// The total size of the last chunk is always "0"
1434 			if (total == 0) {
1435 			    log_debug(_("%s: end of chunks!"), __PRETTY_FUNCTION__);
1436 			    pktsize = 0;
1437 			    done = true;
1438 			    chunks = false;
1439 			} else {
1440 			    pktsize = total+8; // FIXME: why do we need an 8 here ?
1441 // 			    log_debug("%s: Total size for chunk is: %d (%s), adding %d bytes",
1442 // 				      __PRETTY_FUNCTION__, total, bytes, (start - buf->reference()));
1443 			    cygnal::Buffer tmpbuf(start - buf->reference());
1444 			    // don't forget the two bytes for the "\r\n"
1445 			    tmpbuf.copy(buf->reference() + bytes.size() + 2, (start - buf->reference()));
1446 			    buf->clear(); // FIXME: debug only
1447 			    buf->resize(total);
1448  			    buf->copy(tmpbuf.reference(), tmpbuf.size());
1449 			}
1450 		    }
1451 		}
1452 
1453 		// If we got less data than specified, we need to keep reading data
1454 		if (ret < buf->size()) {
1455 		    pktsize -= ret;
1456 		    if (pktsize == 0) {
1457 			done = true;
1458 		    } else {
1459 // 			log_debug("Didn't get a full packet, reading more data");
1460 			continue;
1461 		    }
1462 		}
1463 	    }
1464 	} while (!done);
1465 //  	log_debug("%s: finished packet, ret is %d, pktsize is %d\r\n%s", __PRETTY_FUNCTION__, ret, pktsize,
1466 //  		  hexify(buf->reference(), buf->allocated(), true));
1467 	if (pktsize == 0) {
1468 	    // The last two bytes of the chunk are always "\r\n", which we remove so it
1469 	    // doesn't become part of the binary data being sent in the chunk.
1470 	    if ((*(buf->end()-2) == '\r') && (*(buf->end()-1) == '\n')) {
1471 		*(buf->end()-2) = 0; // '\r'
1472 		*(buf->end()-1) = 0; // '\n'
1473 		buf->setSeekPointer(buf->end() - 2);
1474 	    }
1475 	    _que.push(buf);
1476 	}
1477 	done = false;		// reset
1478     } // end of while chunks
1479 
1480     return _que.size();
1481 }
1482 
1483 int
recvMsg(int fd)1484 HTTP::recvMsg(int fd)
1485 {
1486 //     GNASH_REPORT_FUNCTION;
1487     return recvMsg(fd, 0);
1488 }
1489 
1490 int
recvMsg(int fd,size_t size)1491 HTTP::recvMsg(int fd, size_t size)
1492 {
1493     // GNASH_REPORT_FUNCTION;
1494 
1495     size_t ret = 0;
1496 
1497     if (size == 0) {
1498 	size = cygnal::NETBUFSIZE;
1499     }
1500 
1501     log_debug(_("Starting to wait for data in net for fd #%d"), fd);
1502     Network net;
1503 
1504     do {
1505 	std::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer(size));
1506 	ret = net.readNet(fd, *buf, 5);
1507 //	cerr << __PRETTY_FUNCTION__ << ret << " : " << (char *)buf->reference() << endl;
1508 
1509 	// the read timed out as there was no data, but the socket is still open.
1510  	if (ret == 0) {
1511 	    log_debug(_("no data yet for fd #%d, continuing..."), fd);
1512  	    continue;
1513  	}
1514 	// ret is "no position" when the socket is closed from the other end of the connection,
1515 	// so we're done.
1516 	if ((ret == static_cast<size_t>(string::npos)) || (static_cast<int>(ret) == -1)) {
1517 	    log_debug(_("socket for fd #%d was closed..."), fd);
1518 	    return 0;
1519 	}
1520 	// We got data. Resize the buffer if necessary.
1521 	if (ret > 0) {
1522 	    buf->setSeekPointer(buf->reference() + ret);
1523 //	    cerr << "XXXXX: " << (char *)buf->reference() << endl;
1524  	    if (ret < static_cast<int>(cygnal::NETBUFSIZE)) {
1525 // 		buf->resize(ret);	FIXME: why does this corrupt
1526 // 		the buffer ?
1527 		_que.push(buf);
1528 		break;
1529  	    } else {
1530 		_que.push(buf);
1531 	    }
1532 	    // ret must be more than 0 here
1533 	    if (ret == buf->size()) {
1534 		continue;
1535 	    }
1536 	} else {
1537 	    log_debug(_("no more data for fd #%d, exiting..."), fd);
1538 	    return 0;
1539 	}
1540 	if (static_cast<int>(ret) == -1) {
1541 	    log_debug(_("Handler done for fd #%d, can't read any data..."), fd);
1542 	  return -1;
1543 	}
1544     } while (ret);
1545 
1546     // We're done. Notify the other threads the socket is closed, and tell them to die.
1547     log_debug(_("Done receiving data for fd #%d..."), fd);
1548 
1549     return ret;
1550 }
1551 
1552 
1553 void
dump()1554 HTTP::dump() {
1555 //    GNASH_REPORT_FUNCTION;
1556 
1557     std::lock_guard<std::mutex> lock(stl_mutex);
1558 
1559     log_debug (_("==== The HTTP header breaks down as follows: ===="));
1560     log_debug (_("Filespec: %s"), _filespec.c_str());
1561     log_debug (_("Version: %d.%d"), _version.major, _version.minor);
1562 
1563     std::map<string, string>::const_iterator it;
1564     for (it = _fields.begin(); it != _fields.end(); ++it) {
1565 	log_debug(_("Field: \"%s\" = \"%s\""), it->first, it->second);
1566     }
1567 
1568     // Dump the RTMPT fields
1569     log_debug(_("RTMPT optional index is: "), _index);
1570     log_debug(_("RTMPT optional client ID is: "), _clientid);
1571     log_debug(_("==== ==== ===="));
1572 }
1573 
1574 } // end of gnash namespace
1575 
1576 
1577 // local Variables:
1578 // mode: C++
1579 // indent-tabs-mode: nil
1580 // End:
1581