1
// http.cpp: HyperText Transport Protocol handler for Cygnal, for Gnash.
3
// Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5
// This program is free software; you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation; either version 3 of the License, or
8
// (at your option) any later version.
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
// GNU General Public License for more details.
15
// You should have received a copy of the GNU General Public License
16
// along with this program; if not, write to the Free Software
17
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
#include "gnashconfig.h"
24
#include <boost/thread/mutex.hpp>
25
#include <boost/date_time/gregorian/gregorian.hpp>
26
//#include <boost/date_time/local_time/local_time.hpp>
27
#include <boost/date_time/posix_time/posix_time.hpp>
28
//#include <boost/date_time/time_zone_base.hpp>
32
#include <sys/types.h>
37
using namespace gnash;
40
static boost::mutex stl_mutex;
45
// FIXME, this seems too small to me. --gnu
46
static const int readsize = 1024;
49
: _port(80), _filesize(0), _keepalive(false)
51
// GNASH_REPORT_FUNCTION;
52
// struct status_codes *status = new struct status_codes;
54
// _status_codes(CONTINUE, status);
59
// GNASH_REPORT_FUNCTION;
77
HTTP::operator = (HTTP& /*obj*/)
79
GNASH_REPORT_FUNCTION;
86
HTTP::waitForGetRequest(Network& /*net*/)
88
GNASH_REPORT_FUNCTION;
89
return ""; // TODO: FIXME !
93
HTTP::waitForGetRequest()
95
GNASH_REPORT_FUNCTION;
97
char buffer[readsize+1];
98
memset(buffer, 0, readsize+1);
99
if (readNet(buffer, readsize) > 0) {
100
log_debug (_("Read initial GET Request"));
102
log_error (_("Couldn't read initial GET Request"));
106
extractAccept(buffer);
107
extractMethod(buffer);
108
extractReferer(buffer);
110
extractAgent(buffer);
111
extractLanguage(buffer);
112
extractCharset(buffer);
113
extractConnection(buffer);
114
extractEncoding(buffer);
118
// See if we got a legit GET request
119
if (strncmp(buffer, "GET ", 4) == 0) {
120
log_debug (_("Got legit GET request"));
122
log_error (_("Got bogus GET request"));
130
HTTP::formatHeader(const short type)
132
GNASH_REPORT_FUNCTION;
134
formatHeader(_filesize, type);
139
HTTP::formatHeader(int filesize, const short type)
141
GNASH_REPORT_FUNCTION;
143
_header << "HTTP/1.1 200 OK" << endl;
144
this->formatServer();
146
this->formatConnection("close");
147
// _header << "Accept-Ranges: bytes" << endl;
148
this->formatContentLength(filesize);
149
this->formatContentType();
150
// All HTTP messages are followed by a blank line.
151
this->terminateHeader();
155
HTTP::formatErrorResponse(http_status_e code)
157
GNASH_REPORT_FUNCTION;
159
// First build the message body, so we know how to set Content-Length
160
_body << "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">" << endl;
161
_body << "<html><head>" << endl;
162
_body << "<title>" << code << " Not Found</title>" << endl;
163
_body << "</head><body>" << endl;
164
_body << "<h1>Not Found</h1>" << endl;
165
_body << "<p>The requested URL " << _filespec << " was not found on this server.</p>" << endl;
166
_body << "<hr>" << endl;
167
_body << "<address>Cygnal (GNU/Linux) Server at localhost Port " << _port << " </address>" << endl;
168
_body << "</body></html>" << endl;
171
// First build the header
172
_header << "HTTP/1.1 " << code << " Not Found" << endl;
175
_filesize = _body.str().size();
176
formatContentLength(_filesize);
177
formatConnection("close");
178
formatContentType(HTTP::HTML);
184
GNASH_REPORT_FUNCTION;
185
boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
187
// cout << now.time_of_day() << endl;
189
boost::gregorian::date d(now.date());
190
// boost::gregorian::date d(boost::gregorian::day_clock::local_day());
191
// cout << boost::posix_time::to_simple_string(now) << endl;
192
// cout << d.day_of_week() << endl;
193
// cout << d.day() << endl;
194
// cout << d.year() << endl;
195
// cout << d.month() << endl;
197
// boost::date_time::time_zone_ptr zone(new posix_time_zone("MST"));
198
// boost::date_time::time_zone_base b(now "MST");
199
// cout << zone.dst_zone_abbrev() << endl;
201
_header << "Date: " << d.day_of_week();
202
_header << ", " << d.day();
203
_header << " " << d.month();
204
_header << " " << d.year();
205
_header << " " << now.time_of_day();
206
_header << " GMT" << endl;
213
GNASH_REPORT_FUNCTION;
214
_header << "Server: Cygnal (GNU/Linux)" << endl;
218
HTTP::formatServer(const char *data)
220
GNASH_REPORT_FUNCTION;
221
_header << "Server: " << data << endl;
225
HTTP::formatMethod(const char *data)
227
GNASH_REPORT_FUNCTION;
228
_header << "Method: " << data << endl;
232
HTTP::formatReferer(const char *refer)
234
GNASH_REPORT_FUNCTION;
235
_header << "Referer: " << refer << endl;
239
HTTP::formatConnection(const char *options)
241
GNASH_REPORT_FUNCTION;
242
_header << "Connection: " << options << endl;
246
HTTP::formatContentType()
248
return formatContentType(_filetype);
252
HTTP::formatContentType(filetype_e filetype)
254
// GNASH_REPORT_FUNCTION;
258
_header << "Content-Type: text/html; charset=UTF-8" << endl;
261
_header << "Content-Type: application/x-shockwave-flash" << endl;
262
// _header << "Content-Type: application/futuresplash" << endl;
265
_header << "Content-Type: video/flv" << endl;
268
_header << "Content-Type: audio/mpeg" << endl;
271
_header << "Content-Type: text/html; charset=UTF-8" << endl;
276
HTTP::formatContentLength()
278
// GNASH_REPORT_FUNCTION;
279
_header << "Content-Length: " << _filesize << endl;
283
HTTP::formatContentLength(int filesize)
285
// GNASH_REPORT_FUNCTION;
286
_header << "Content-Length: " << filesize << endl;
290
HTTP::formatHost(const char *host)
292
// GNASH_REPORT_FUNCTION;
293
_header << "Host: " << host << endl;
297
HTTP::formatAgent(const char *agent)
299
// GNASH_REPORT_FUNCTION;
300
_header << "User-Agent: " << agent << endl;
304
HTTP::formatLanguage(const char *lang)
306
// GNASH_REPORT_FUNCTION;
308
// For some browsers this appears to also be Content-Language
309
_header << "Accept-Language: " << lang << endl;
313
HTTP::formatCharset(const char *set)
315
GNASH_REPORT_FUNCTION;
316
// For some browsers this appears to also be Content-Charset
317
_header << "Accept-Charset: " << set << endl;
321
HTTP::formatEncoding(const char *code)
323
GNASH_REPORT_FUNCTION;
324
_header << "Accept-Encoding: " << code << endl;
328
HTTP::formatTE(const char *te)
330
GNASH_REPORT_FUNCTION;
331
_header << "TE: " << te << endl;
335
HTTP::sendGetReply(http_status_e code)
337
GNASH_REPORT_FUNCTION;
339
formatHeader(_filesize, HTML);
340
int ret = writeNet(_header.str().c_str(), _header.str().size());
341
if ( _body.str().size() > 0) {
342
ret += writeNet(_body.str().c_str(), _body.str().size());
346
log_debug (_("Sent GET Reply"));
347
// log_debug (_("Sent GET Reply: %s"), _header.str().c_str());
350
log_debug (_("Couldn't send GET Reply, writeNet returned %d"), ret);
353
// cout << "GET Header is:" << endl << _header.str() << endl;
354
return true; // Default to true
358
HTTP::formatRequest(const char *url, http_method_e req)
360
GNASH_REPORT_FUNCTION;
364
_header << req << " " << url << "HTTP/1.1" << endl;
365
_header << "User-Agent: Opera/9.01 (X11; Linux i686; U; en)" << endl;
366
_header << "Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1" << endl;
368
_header << "Accept-Language: en" << endl;
369
_header << "Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1" << endl;
371
_header << "Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0" << endl;
372
_header << "Referer: " << url << endl;
374
_header << "Connection: Keep-Alive, TE" << endl;
375
_header << "TE: deflate, gzip, chunked, identity, trailers" << endl;
379
// HTTP::sendGetReply(Network &net)
381
// GNASH_REPORT_FUNCTION;
384
// This is what a GET request looks like.
385
// GET /software/gnash/tests/flvplayer2.swf?file=http://localhost:4080/software/gnash/tests/lulutest.flv HTTP/1.1
386
// User-Agent: Opera/9.01 (X11; Linux i686; U; en)
387
// Host: localhost:4080
388
// Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
389
// Accept-Language: en
390
// Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
391
// Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0
392
// Referer: http://localhost/software/gnash/tests/
393
// Connection: Keep-Alive, TE
394
// TE: deflate, gzip, chunked, identity, trailers
396
HTTP::extractAccept(const char *data) {
397
// GNASH_REPORT_FUNCTION;
400
string::size_type start, end, length, pos;
401
string pattern = "Accept: ";
403
start = body.find(pattern, 0);
404
if (start == string::npos) {
407
end = body.find("\r\n", start);
408
if (end == string::npos) {
409
end = body.find("\n", start);
413
length = end-start-pattern.size();
414
start = start+pattern.size();
417
pos = (body.find(",", start) + 2);
419
return _encoding.size();
421
if ((pos == string::npos) || (pos > end)) {
422
length = end - start;
424
length = pos - start - 2;
426
string substr = body.substr(start, length);
427
// printf("FIXME: \"%s\"\n", substr.c_str());
428
_accept.push_back(substr);
432
return _accept.size();
436
HTTP::extractMethod(const char *data) {
437
// GNASH_REPORT_FUNCTION;
439
boost::mutex::scoped_lock lock(stl_mutex);
441
string::size_type start, end;
444
length = body.size();
445
start = body.find(" ", 0);
446
if (start == string::npos) {
449
_method = body.substr(0, start);
450
end = body.find(" ", start+1);
451
if (end == string::npos) {
454
_url = body.substr(start+1, end-start-1);
455
_version = body.substr(end+1, length);
457
end = _url.find("?", 0);
458
// _filespec = _url.substr(start+1, end);
463
HTTP::extractReferer(const char *data) {
464
// GNASH_REPORT_FUNCTION;
467
string::size_type start, end;
468
string pattern = "Referer: ";
470
start = body.find(pattern, 0);
471
if (start == string::npos) {
474
end = body.find("\r\n", start);
475
if (end == string::npos) {
479
_referer = body.substr(start+pattern.size(), end-start-1);
484
HTTP::extractConnection(const char *data) {
485
// GNASH_REPORT_FUNCTION;
488
string::size_type start, end, length, pos;
489
string pattern = "Connection: ";
491
start = body.find(pattern, 0);
492
if (start == string::npos) {
495
end = body.find("\r\n", start);
496
if (end == string::npos) {
497
end = body.find("\n", start);
501
length = end-start-pattern.size();
502
start = start+pattern.size();
503
string _connection = body.substr(start, length);
506
pos = (body.find(",", start) + 2);
508
return _encoding.size();
510
if ((pos == string::npos) || (pos > end)) {
511
length = end - start;
513
length = pos - start - 2;
515
string substr = body.substr(start, length);
516
// printf("FIXME: \"%s\"\n", substr.c_str());
517
_connections.push_back(substr);
518
if (substr == "Keep-Alive") {
524
return _connections.size();
528
HTTP::extractHost(const char *data) {
529
// GNASH_REPORT_FUNCTION;
532
string::size_type start, end;
533
string pattern = "Host: ";
535
start = body.find(pattern, 0);
536
if (start == string::npos) {
539
end = body.find("\r\n", start);
540
if (end == string::npos) {
544
_host = body.substr(start+pattern.size(), end-start-1);
549
HTTP::extractAgent(const char *data) {
550
// GNASH_REPORT_FUNCTION;
553
string::size_type start, end;
554
string pattern = "User-Agent: ";
556
start = body.find(pattern, 0);
557
if (start == string::npos) {
560
end = body.find("\r\n", start);
561
if (end == string::npos) {
565
_agent = body.substr(start+pattern.size(), end-start-1);
570
HTTP::extractLanguage(const char *data) {
571
// GNASH_REPORT_FUNCTION;
574
string::size_type start, end, length, pos, terminate;
575
// match both Accept-Language and Content-Language
576
string pattern = "-Language: ";
578
start = body.find(pattern, 0);
579
if (start == string::npos) {
582
end = body.find("\r\n", start);
583
if (end == string::npos) {
584
end = body.find("\n", start);
587
length = end-start-pattern.size();
588
start = start+pattern.size();
590
terminate = (body.find(";", start));
591
if (terminate == string::npos) {
596
pos = (body.find(",", start));
598
return _encoding.size();
600
if ((pos == string::npos) || (pos >= terminate)) {
601
length = terminate - start;
603
length = pos - start;
605
string substr = body.substr(start, length);
606
// printf("FIXME: \"%s\"\n", substr.c_str());
607
_language.push_back(substr);
611
// _language = body.substr(start+pattern.size(), end-start-1);
612
return _language.size();
616
HTTP::extractCharset(const char *data) {
617
// GNASH_REPORT_FUNCTION;
620
string::size_type start, end, length, pos, terminate;
621
// match both Accept-Charset and Content-Charset
622
string pattern = "-Charset: ";
624
start = body.find(pattern, 0);
625
if (start == string::npos) {
628
end = body.find("\r\n", start);
629
if (end == string::npos) {
630
end = body.find("\n", start);
634
length = end-start-pattern.size();
635
start = start+pattern.size();
636
string _connection = body.substr(start, length);
638
terminate = (body.find(";", start));
639
if (terminate == string::npos) {
643
pos = (body.find(",", start) + 2);
645
return _encoding.size();
647
if ((pos == string::npos) || (pos >= terminate)) {
648
length = terminate - start;
650
length = pos - start - 2;
652
string substr = body.substr(start, length);
653
// printf("FIXME: \"%s\"\n", substr.c_str());
654
_charset.push_back(substr);
657
// _charset = body.substr(start+pattern.size(), end-start-1);
658
return _charset.size();
662
HTTP::extractEncoding(const char *data) {
663
// GNASH_REPORT_FUNCTION;
666
string::size_type start, end, length, pos, terminate;
667
// match both Accept-Encoding and Content-Encoding
668
string pattern = "-Encoding: ";
670
start = body.find(pattern, 0);
671
if (start == string::npos) {
674
end = body.find("\r\n", start);
675
if (end == string::npos) {
676
end = body.find("\n", start);
680
length = end-start-pattern.size();
681
start = start+pattern.size();
682
string _connection = body.substr(start, length);
684
// Drop anything after a ';' character
685
terminate = (body.find(";", start));
686
if (terminate == string::npos) {
690
pos = (body.find(",", start) + 2);
692
return _encoding.size();
694
if ((pos == string::npos) || (pos >= terminate)) {
695
length = terminate - start;
697
length = pos - start - 2;
699
string substr = body.substr(start, length);
700
// printf("FIXME: \"%s\"\n", substr.c_str());
701
_encoding.push_back(substr);
705
// _encoding = body.substr(start+pattern.size(), end-start-1);
706
return _encoding.size();
710
HTTP::extractTE(const char *data) {
711
// GNASH_REPORT_FUNCTION;
714
string::size_type start, end, length, pos;
715
string pattern = "TE: ";
717
start = body.find(pattern, 0);
718
if (start == string::npos) {
721
end = body.find("\r\n", start);
722
if (end == string::npos) {
723
end = body.find("\n", start);
727
length = end-start-pattern.size();
728
start = start+pattern.size();
731
pos = (body.find(",", start));
733
return _encoding.size();
735
if ((pos == string::npos) || (pos >= end)) {
736
length = end - start;
738
length = pos - start;
740
string substr = body.substr(start, length);
741
// printf("FIXME: \"%s\"\n", substr.c_str());
742
_te.push_back(substr);
748
// Get the file type, so we know how to set the
749
// Content-type in the header.
751
HTTP::getFileStats(std::string &filespec)
753
GNASH_REPORT_FUNCTION;
754
bool try_again = true;
755
string actual_filespec = filespec;
760
// cerr << "Trying to open " << actual_filespec << endl;
761
if (stat(actual_filespec.c_str(), &st) == 0) {
762
// If it's a directory, then we emulate what apache
763
// does, which is to load the index.html file in that
764
// directry if it exists.
765
if (S_ISDIR(st.st_mode)) {
766
log_debug("%s is a directory\n", actual_filespec.c_str());
767
if (actual_filespec[actual_filespec.size()-1] != '/') {
768
actual_filespec += '/';
770
actual_filespec += "index.html";
773
} else { // not a directory
774
log_debug("%s is not a directory\n", actual_filespec.c_str());
775
string::size_type pos;
776
pos = filespec.rfind(".");
777
if (pos != string::npos) {
778
string suffix = filespec.substr(pos, filespec.size());
779
if (suffix == "html") {
781
log_debug("HTML content found");
783
if (suffix == "swf") {
785
log_debug("SWF content found");
787
if (suffix == "flv") {
789
log_debug("FLV content found");
791
if (suffix == "mp3") {
793
log_debug("MP3 content found");
798
_filetype = HTTP::ERROR;
800
} // end of try_waiting
802
_filesize = st.st_size;
808
GNASH_REPORT_FUNCTION;
810
boost::mutex::scoped_lock lock(stl_mutex);
811
vector<string>::iterator it;
813
log_debug (_("==== The HTTP header breaks down as follows: ===="));
814
log_debug (_("Filespec: %s"), _filespec.c_str());
815
log_debug (_("URL: %s"), _url.c_str());
816
log_debug (_("Version: %s"), _version.c_str());
817
for (it = _accept.begin(); it != _accept.end(); it++) {
818
log_debug("Accept param: \"%s\"", (*(it)).c_str());
820
log_debug (_("Method: %s"), _method.c_str());
821
log_debug (_("Referer: %s"), _referer.c_str());
822
log_debug (_("Connections:"));
823
for (it = _connections.begin(); it != _connections.end(); it++) {
824
log_debug("Connection param is: \"%s\"", (*(it)).c_str());
826
log_debug (_("Host: %s"), _host.c_str());
827
log_debug (_("User Agent: %s"), _agent.c_str());
828
for (it = _language.begin(); it != _language.end(); it++) {
829
log_debug("Language param: \"%s\"", (*(it)).c_str());
831
for (it = _charset.begin(); it != _charset.end(); it++) {
832
log_debug("Charset param: \"%s\"", (*(it)).c_str());
834
for (it = _encoding.begin(); it != _encoding.end(); it++) {
835
log_debug("Encodings param: \"%s\"", (*(it)).c_str());
837
for (it = _te.begin(); it != _te.end(); it++) {
838
log_debug("TE param: \"%s\"", (*(it)).c_str());
840
log_debug (_("==== ==== ===="));
843
} // end of cygnal namespace
848
// indent-tabs-mode: t