~ubuntu-branches/ubuntu/precise/gnash/precise-proposed

« back to all changes in this revision

Viewing changes to .pc/fixtypos.patch/cygnal/http_server.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Angel Abad, Angel Abad, Micah Gersten
  • Date: 2010-11-28 22:18:48 UTC
  • mfrom: (3.1.10 sid)
  • Revision ID: james.westby@ubuntu.com-20101128221848-apjipwy78m13612a
Tags: 0.8.8-6ubuntu1
[ Angel Abad <angelabad@ubuntu.com> ]
* Merge from debian unstable (LP: #682386). Remaining changes:
  - Add Ubuntu flash alternatives in postinst and prerm
    + update debian/browser-plugin-gnash.postinst
    + update debian/browser-plugin-gnash.prerm

[ Micah Gersten <micahg@ubuntu.com> ]
* Only install the flash alternative in /usr/lib/mozilla/plugins as the other
  locations are deprecated
  - update debian/browser-plugin-gnash.postinst

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// http.cpp:  HyperText Transport Protocol handler for Cygnal, for Gnash.
2
 
// 
3
 
//   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
4
 
//   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 <boost/thread/mutex.hpp>
26
 
#include <boost/shared_ptr.hpp>
27
 
#include <boost/shared_array.hpp>
28
 
#include <boost/scoped_array.hpp>
29
 
#include <boost/tokenizer.hpp>
30
 
#include <boost/date_time/posix_time/posix_time.hpp>
31
 
#include <boost/date_time/gregorian/gregorian.hpp>
32
 
#include <sys/types.h>
33
 
#include <sys/stat.h>
34
 
#include <fcntl.h>
35
 
#include <string>
36
 
#include <iostream>
37
 
#include <cstring>
38
 
#include <sstream>
39
 
#include <sys/types.h>
40
 
#include <sys/stat.h>
41
 
#include <algorithm>
42
 
#include "GnashSystemIOHeaders.h" // read()
43
 
 
44
 
#include "amf.h"
45
 
#include "element.h"
46
 
#include "cque.h"
47
 
#include "log.h"
48
 
#include "crc.h"
49
 
#include "network.h"
50
 
//#include "handler.h"
51
 
#include "utility.h"
52
 
#include "buffer.h"
53
 
#include "http.h"
54
 
#include "diskstream.h"
55
 
 
56
 
// Cygnal specific headers
57
 
#include "http_server.h"
58
 
#include "proc.h"
59
 
#include "cache.h"
60
 
 
61
 
// Not POSIX, so best not rely on it if possible.
62
 
#ifndef PATH_MAX
63
 
# define PATH_MAX 1024
64
 
#endif
65
 
 
66
 
using namespace gnash;
67
 
using namespace std;
68
 
 
69
 
static boost::mutex stl_mutex;
70
 
 
71
 
namespace cygnal
72
 
{
73
 
 
74
 
// The rcfile is loaded and parsed here:
75
 
static CRcInitFile& crcfile = CRcInitFile::getDefaultInstance();
76
 
static Cache& cache = Cache::getDefaultInstance();
77
 
// static Proc& cgis = Proc::getDefaultInstance();
78
 
 
79
 
HTTPServer::HTTPServer() 
80
 
{
81
 
//    GNASH_REPORT_FUNCTION;
82
 
}
83
 
 
84
 
HTTPServer::~HTTPServer()
85
 
{
86
 
//    GNASH_REPORT_FUNCTION;
87
 
}
88
 
 
89
 
HTTP::http_method_e
90
 
HTTPServer::processClientRequest(int fd)
91
 
{
92
 
    GNASH_REPORT_FUNCTION;
93
 
    
94
 
    cygnal::Buffer *buf = new cygnal::Buffer;
95
 
 
96
 
    // return processClientRequest(fd, buf);
97
 
    return HTTP::HTTP_NONE;
98
 
}
99
 
 
100
 
HTTP::http_method_e
101
 
HTTPServer::processClientRequest(Handler *hand, int fd, cygnal::Buffer *buf)
102
 
{
103
 
    GNASH_REPORT_FUNCTION;
104
 
    
105
 
    cygnal::Buffer result;
106
 
 
107
 
    if (buf) {
108
 
        _cmd = extractCommand(buf->reference());
109
 
        switch (_cmd) {
110
 
          case HTTP::HTTP_GET:
111
 
              result = processGetRequest(hand, fd, buf);
112
 
              break;
113
 
          case HTTP::HTTP_POST:
114
 
              result = processPostRequest(fd, buf);
115
 
              break;
116
 
          case HTTP::HTTP_HEAD:
117
 
              result = processHeadRequest(fd, buf);
118
 
              break;
119
 
          case HTTP::HTTP_CONNECT:
120
 
              result = processConnectRequest(fd, buf);
121
 
              break;
122
 
          case HTTP::HTTP_TRACE:
123
 
              result = processTraceRequest(fd, buf);
124
 
              break;
125
 
          case HTTP::HTTP_OPTIONS:
126
 
              result = processOptionsRequest(fd, buf);
127
 
              break;
128
 
          case HTTP::HTTP_PUT:
129
 
              result = processPutRequest(fd, buf);
130
 
              break;
131
 
          case HTTP::HTTP_DELETE:
132
 
              result = processDeleteRequest(fd, buf);
133
 
              break;
134
 
          default:
135
 
              break;
136
 
        }
137
 
    }
138
 
 
139
 
#if 0
140
 
 
141
 
    writeNet(fd, result);
142
 
 
143
 
    _docroot = crcfile.getDocumentRoot();
144
 
    
145
 
    string url = _docroot + _filespec;
146
 
    
147
 
    // See if the file is in the cache and already opened.
148
 
    boost::shared_ptr<DiskStream> filestream(cache.findFile(_filespec));
149
 
    if (filestream) {
150
 
        log_debug("FIXME: found filestream %s in cache!", _filespec);
151
 
        filestream->dump();
152
 
    } else {
153
 
        filestream.reset(new DiskStream);
154
 
        log_network("New filestream %s", _filespec);
155
 
        // cache.addFile(url, filestream);      FIXME: always reload from disk for now.
156
 
        
157
 
        // Oopen the file and read the first chunk into memory
158
 
        if (filestream->open(url)) {
159
 
            formatErrorResponse(HTTPServer::NOT_FOUND);
160
 
        } else {
161
 
            // Get the file size for the HTTPServer header
162
 
            if (filestream->getFileType() == DiskStream::FILETYPE_NONE) {
163
 
                formatErrorResponse(HTTPServer::NOT_FOUND);
164
 
            } else {
165
 
                // cache.addPath(_filespec, filestream->getFilespec());
166
 
                // cache.addFile(_filespec, filestream);
167
 
            }
168
 
        }
169
 
        // Close the file but leave resident for now.
170
 
        if (filestream->fullyPopulated()) {
171
 
            filestream->close();
172
 
        }
173
 
//      cache.addFile(_filespec, filestream);
174
 
    }
175
 
#endif
176
 
    
177
 
    return _cmd;
178
 
}
179
 
 
180
 
// A GET request asks the server to send a file to the client
181
 
cygnal::Buffer &
182
 
HTTPServer::processGetRequest(Handler *hand, int fd, cygnal::Buffer *buf)
183
 
{
184
 
    GNASH_REPORT_FUNCTION;
185
 
 
186
 
    // cerr << "QUE = " << _que.size() << endl;
187
 
    
188
 
//    cerr << "YYYYYYY: " << (char *)buf->reference() << endl;
189
 
//    cerr << hexify(buf->reference(), buf->allocated(), false) << endl;
190
 
    
191
 
    if (buf == 0) {
192
 
     // log_debug("Que empty, net connection dropped for fd #%d", getFileFd());
193
 
        log_debug("Que empty, net connection dropped for fd #%d", fd);
194
 
        cygnal::Buffer buf;
195
 
        return buf;
196
 
    }
197
 
    
198
 
    clearHeader();
199
 
    processHeaderFields(buf);
200
 
 
201
 
    _docroot = crcfile.getDocumentRoot();
202
 
    
203
 
    string url = _docroot + _filespec;
204
 
 
205
 
    boost::shared_ptr<DiskStream> ds = hand->getDiskStream(fd);
206
 
    if (ds) {
207
 
        _diskstream = ds;
208
 
    }
209
 
    if (!_diskstream) {
210
 
        _diskstream.reset(new DiskStream);
211
 
        log_network("New filestream %s", _filespec);
212
 
    } else {
213
 
        log_network("Reusing filestream %s", _filespec);
214
 
    }
215
 
    
216
 
    // Oopen the file and read the first chunk into memory
217
 
    if (_diskstream->open(url)) {
218
 
        formatErrorResponse(HTTPServer::NOT_FOUND);
219
 
    } else {
220
 
        // Get the file size for the HTTPServer header
221
 
        if (_diskstream->getFileType() == DiskStream::FILETYPE_NONE) {
222
 
            formatErrorResponse(HTTPServer::NOT_FOUND);
223
 
        } else {
224
 
            // cache.addPath(_filespec, filestream->getFilespec());
225
 
            // cache.addFile(_filespec, filestream);
226
 
        }
227
 
    }
228
 
    // Closing the file closes the disk file, but leaves data resident
229
 
    // in memory for future access to this file. If we've been opened,
230
 
    // the next operation is to start writing the file next time
231
 
    // ::play() is called.
232
 
    if (_diskstream->fullyPopulated()) {
233
 
        _diskstream->close();
234
 
    }
235
 
    _diskstream->setState(DiskStream::PLAY);
236
 
//      cache.addFile(_filespec, _diskstream);
237
 
 
238
 
    // Create the reply message
239
 
//     _close = true; Force sending the close connection in the header
240
 
    cygnal::Buffer &reply = formatHeader(_diskstream->getFileType(),
241
 
                                      _diskstream->getFileSize(),
242
 
                                      HTTPServer::OK);
243
 
 
244
 
    writeNet(fd, reply);
245
 
 
246
 
    size_t filesize = _diskstream->getFileSize();
247
 
    // size_t bytes_read = 0;
248
 
    // int ret;
249
 
    // size_t page = 0;
250
 
    if (filesize) {
251
 
#ifdef USE_STATS_CACHE
252
 
        struct timespec start;
253
 
        clock_gettime (CLOCK_REALTIME, &start);
254
 
#endif  
255
 
        
256
 
#ifdef USE_STATS_CACHE
257
 
        struct timespec end;
258
 
        clock_gettime (CLOCK_REALTIME, &end);
259
 
        double time = (end.tv_sec - start.tv_sec) + ((end.tv_nsec - start.tv_nsec)/1e9);
260
 
        cerr << "File " << _filespec
261
 
             << " transferred " << filesize << " bytes in: " << fixed
262
 
             << time << " seconds for net fd #" << fd << endl;
263
 
#endif
264
 
    }
265
 
    
266
 
    return reply;
267
 
}
268
 
 
269
 
// A POST request asks sends a data from the client to the server. After processing
270
 
// the header like we normally do, we then read the amount of bytes specified by
271
 
// the "content-length" field, and then write that data to disk, or decode the amf.
272
 
boost::shared_ptr<cygnal::Buffer>
273
 
HTTPServer::processPostRequest(int fd, cygnal::Buffer *bufFIXME)
274
 
{
275
 
    GNASH_REPORT_FUNCTION;
276
 
 
277
 
//    cerr << "QUE1 = " << _que.size() << endl;
278
 
 
279
 
    boost::shared_ptr<cygnal::Buffer> buf;
280
 
    
281
 
    if (_que.size() == 0) {
282
 
        return buf;
283
 
    }
284
 
    
285
 
    buf = _que.pop();
286
 
    if (buf == 0) {
287
 
        log_debug("Que empty, net connection dropped for fd #%d", getFileFd());
288
 
        return buf;
289
 
    }
290
 
//    cerr << __FUNCTION__ << buf->allocated() << " : " << hexify(buf->reference(), buf->allocated(), true) << endl;
291
 
    
292
 
    clearHeader();
293
 
    boost::uint8_t *data = processHeaderFields(buf.get());
294
 
    size_t length = strtol(getField("content-length").c_str(), NULL, 0);
295
 
    boost::shared_ptr<cygnal::Buffer> content(new cygnal::Buffer(length));
296
 
    int ret = 0;
297
 
    if (buf->allocated() - (data - buf->reference()) ) {
298
 
//      cerr << "Don't need to read more data: have " << buf->allocated() << " bytes" << endl;
299
 
        content->copy(data, length);
300
 
        ret = length;
301
 
    } else {    
302
 
//      cerr << "Need to read more data, only have "  << buf->allocated() << " bytes" << endl;
303
 
        ret = readNet(fd, *content, 2);
304
 
        data = content->reference();
305
 
    }    
306
 
    
307
 
    if (getField("content-type") == "application/x-www-form-urlencoded") {
308
 
        log_debug("Got file data in POST");
309
 
        string url = _docroot + _filespec;
310
 
        DiskStream ds(url, *content);
311
 
        ds.writeToDisk();
312
 
//    ds.close();
313
 
        // oh boy, we got ourselves some encoded AMF objects instead of a boring file.
314
 
    } else if (getField("content-type") == "application/x-amf") {
315
 
        log_debug("Got AMF data in POST");
316
 
#if 0
317
 
        amf::AMF amf;
318
 
        boost::shared_ptr<cygnal::Element> el = amf.extractAMF(content.reference(), content.end());
319
 
        el->dump();             // FIXME: do something intelligent
320
 
                                // with this Element
321
 
#endif
322
 
    }
323
 
    
324
 
    // Send the reply
325
 
 
326
 
    // NOTE: this is a "special" path we trap until we have real CGI support
327
 
    if ((getField("content-type") == "application/x-amf")
328
 
        && (getField("content-type") == "application/x-amf")) {
329
 
#ifdef USE_CGIBIN
330
 
        if (_filespec == "/echo/gateway") {
331
 
        }
332
 
        
333
 
        Proc cgis;
334
 
        string path = _docroot;
335
 
        path += _filespec;
336
 
        cgis.startCGI(_filespec, true, CGIBIN_PORT);
337
 
        cgis.createClient("localhost", CGIBIN_PORT);
338
 
        cgis.writeNet(*content);
339
 
        boost::shared_ptr<cygnal::Buffer> reply = cgis.readNet();
340
 
        
341
 
        writeNet(fd, *reply);
342
 
//      cgis.stopCGI(_filespec);
343
 
#else
344
 
        vector<boost::shared_ptr<cygnal::Element> > headers = parseEchoRequest(*content);
345
 
        //boost::shared_ptr<cygnal::Element> &el0 = headers[0];
346
 
 
347
 
        if (headers.size() >= 4) {
348
 
            if (headers[3]) {
349
 
                cygnal::Buffer &reply = formatEchoResponse(headers[1]->getName(), *headers[3]);
350
 
//          cerr << "FIXME 3: " << hexify(reply.reference(), reply.allocated(), true) << endl;
351
 
//          cerr << "FIXME 3: " << hexify(reply.reference(), reply.allocated(), false) << endl;
352
 
                writeNet(fd, reply);
353
 
            }
354
 
        }
355
 
#endif
356
 
    } else {
357
 
        cygnal::Buffer &reply = formatHeader(_filetype, _filesize, HTTPServer::OK);
358
 
        writeNet(fd, reply);
359
 
    }
360
 
 
361
 
    return buf;
362
 
}
363
 
 
364
 
boost::shared_ptr<cygnal::Buffer>
365
 
HTTPServer::processPutRequest(int /* fd */, cygnal::Buffer */* buf */)
366
 
{
367
 
    boost::shared_ptr<cygnal::Buffer> buf;
368
 
//    GNASH_REPORT_FUNCTION;
369
 
    log_unimpl("PUT request");
370
 
 
371
 
    return buf;
372
 
}
373
 
 
374
 
boost::shared_ptr<cygnal::Buffer> 
375
 
HTTPServer::processDeleteRequest(int /* fd */, cygnal::Buffer */* buf */)
376
 
{
377
 
//    GNASH_REPORT_FUNCTION;
378
 
    boost::shared_ptr<cygnal::Buffer> buf;
379
 
    log_unimpl("DELETE request");
380
 
    
381
 
    return buf;
382
 
}
383
 
 
384
 
boost::shared_ptr<cygnal::Buffer> 
385
 
HTTPServer::processConnectRequest(int /* fd */, cygnal::Buffer */* buf */)
386
 
{
387
 
//    GNASH_REPORT_FUNCTION;
388
 
    boost::shared_ptr<cygnal::Buffer> buf;
389
 
    log_unimpl("CONNECT request");
390
 
 
391
 
    return buf;
392
 
}
393
 
 
394
 
boost::shared_ptr<cygnal::Buffer>
395
 
HTTPServer::processOptionsRequest(int /* fd */, cygnal::Buffer */* buf */)
396
 
{
397
 
//    GNASH_REPORT_FUNCTION;
398
 
    boost::shared_ptr<cygnal::Buffer> buf;
399
 
    log_unimpl("OPTIONS request");
400
 
 
401
 
    return buf;
402
 
}
403
 
 
404
 
boost::shared_ptr<cygnal::Buffer>
405
 
HTTPServer::processHeadRequest(int /* fd */, cygnal::Buffer */* buf */)
406
 
{
407
 
//    GNASH_REPORT_FUNCTION;
408
 
    boost::shared_ptr<cygnal::Buffer> buf;
409
 
    log_unimpl("HEAD request");
410
 
    
411
 
    return buf;
412
 
}
413
 
 
414
 
boost::shared_ptr<cygnal::Buffer>
415
 
HTTPServer::processTraceRequest(int /* fd */, cygnal::Buffer */* buf */)
416
 
{
417
 
//    GNASH_REPORT_FUNCTION;
418
 
    boost::shared_ptr<cygnal::Buffer> buf;
419
 
    log_unimpl("TRACE request");
420
 
    
421
 
    return buf;
422
 
}
423
 
 
424
 
cygnal::Buffer &
425
 
HTTPServer::formatErrorResponse(http_status_e code)
426
 
{
427
 
//    GNASH_REPORT_FUNCTION;
428
 
 
429
 
    char num[12];
430
 
    // First build the message body, so we know how to set Content-Length
431
 
    _buffer += "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n";
432
 
    _buffer += "<html><head>\r\n";
433
 
    _buffer += "<title>";
434
 
    sprintf(num, "%d", code);
435
 
    _buffer += num;
436
 
    _buffer += " Not Found</title>\r\n";
437
 
    _buffer += "</head><body>\r\n";
438
 
    _buffer += "<h1>Not Found</h1>\r\n";
439
 
    _buffer += "<p>The requested URL ";
440
 
    _buffer += _filespec;
441
 
    _buffer += " was not found on this server.</p>\r\n";
442
 
    _buffer += "<hr>\r\n";
443
 
    _buffer += "<address>Cygnal (GNU/Linux) Server at ";
444
 
    _buffer += getField("host");
445
 
    _buffer += " </address>\r\n";
446
 
    _buffer += "</body></html>\r\n";
447
 
 
448
 
    // First build the header
449
 
    formatDate();
450
 
    formatServer();
451
 
    formatContentLength(_filesize);
452
 
    formatConnection("close");
453
 
    formatContentType(_filetype);
454
 
 
455
 
    // All HTTPServer messages are followed by a blank line.
456
 
    terminateHeader();
457
 
 
458
 
    return _buffer;
459
 
}
460
 
 
461
 
cygnal::Buffer &
462
 
HTTPServer::formatGetReply(http_status_e code)
463
 
{
464
 
 
465
 
//    GNASH_REPORT_FUNCTION;
466
 
    
467
 
    return formatHeader(_filesize, code);
468
 
}
469
 
 
470
 
cygnal::Buffer &
471
 
HTTPServer::formatGetReply(size_t size, http_status_e code)
472
 
{
473
 
//    GNASH_REPORT_FUNCTION;
474
 
    
475
 
    formatHeader(size, code);
476
 
    
477
 
//    int ret = Network::writeNet(_header.str());    
478
 
//    boost::uint8_t *ptr = (boost::uint8_t *)_body.str().c_str();
479
 
//     buf->copy(ptr, _body.str().size());
480
 
//    _handler->dump();
481
 
 
482
 
#if 0
483
 
    if (_header.str().size()) {
484
 
        log_debug (_("Sent GET Reply"));
485
 
        return _buffer;
486
 
    } else {
487
 
        clearHeader();
488
 
        log_debug (_("Couldn't send GET Reply, no header data"));
489
 
    }    
490
 
#endif
491
 
    
492
 
    return _buffer;
493
 
}
494
 
 
495
 
cygnal::Buffer &
496
 
HTTPServer::formatPostReply(rtmpt_cmd_e /* code */)
497
 
{
498
 
    GNASH_REPORT_FUNCTION;
499
 
 
500
 
    formatDate();
501
 
    formatServer();
502
 
    formatContentType(DiskStream::FILETYPE_AMF);
503
 
    // All HTTPServer messages are followed by a blank line.
504
 
    terminateHeader();
505
 
    return _buffer;
506
 
 
507
 
#if 0
508
 
    formatHeader(_filesize, code);
509
 
    boost::shared_ptr<cygnal::Buffer> buf = new cygnal::Buffer;
510
 
    if (_header.str().size()) {
511
 
        buf->resize(_header.str().size());
512
 
        string str = _header.str();
513
 
        buf->copy(str);
514
 
        _handler->pushout(buf);
515
 
        _handler->notifyout();
516
 
        log_debug (_("Sent GET Reply"));
517
 
        return true; // Default to true
518
 
    } else {
519
 
        clearHeader();
520
 
        log_debug (_("Couldn't send POST Reply, no header data"));
521
 
    }
522
 
#endif
523
 
 
524
 
    return _buffer;
525
 
}
526
 
 
527
 
#ifndef USE_CGIBIN
528
 
// Parse an Echo Request message coming from the Red5 echo_test. This
529
 
// method should only be used for testing purposes.
530
 
vector<boost::shared_ptr<cygnal::Element > >
531
 
HTTPServer::parseEchoRequest(boost::uint8_t *data, size_t size)
532
 
{
533
 
//    GNASH_REPORT_FUNCTION;
534
 
    
535
 
    vector<boost::shared_ptr<cygnal::Element > > headers;
536
 
        
537
 
    // skip past the header bytes, we don't care about them.
538
 
    boost::uint8_t *tmpptr = data + 6;
539
 
    
540
 
    boost::uint16_t length;
541
 
    length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
542
 
    tmpptr += sizeof(boost::uint16_t);
543
 
 
544
 
    // Get the first name, which is a raw string, and not preceded by
545
 
    // a type byte.
546
 
    boost::shared_ptr<cygnal::Element > el1(new cygnal::Element);
547
 
    
548
 
    // If the length of the name field is corrupted, then we get out of
549
 
    // range quick, and corrupt memory. This is a bit of a hack, but
550
 
    // reduces memory errors caused by some of the corrupted tes cases.
551
 
    boost::uint8_t *endstr = std::find(tmpptr, tmpptr+length, '\0');
552
 
    if (endstr != tmpptr+length) {
553
 
        log_debug("Caught corrupted string! length was %d, null at %d",
554
 
                  length,  endstr-tmpptr);
555
 
        length = endstr-tmpptr;
556
 
    }
557
 
    el1->setName(tmpptr, length);
558
 
    tmpptr += length;
559
 
    headers.push_back(el1);
560
 
    
561
 
    // Get the second name, which is a raw string, and not preceded by
562
 
    // a type byte.
563
 
    length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
564
 
    tmpptr += sizeof(boost::uint16_t);
565
 
    boost::shared_ptr<cygnal::Element > el2(new cygnal::Element);
566
 
 
567
 
//     std::string name2(reinterpret_cast<const char *>(tmpptr), length);
568
 
//     el2->setName(name2.c_str(), name2.size());
569
 
    // If the length of the name field is corrupted, then we get out of
570
 
    // range quick, and corrupt memory. This is a bit of a hack, but
571
 
    // reduces memory errors caused by some of the corrupted tes cases.
572
 
    endstr = std::find(tmpptr, tmpptr+length, '\0');
573
 
    if (endstr != tmpptr+length) {
574
 
        log_debug("Caught corrupted string! length was %d, null at %d",
575
 
                  length,  endstr-tmpptr);
576
 
        length = endstr-tmpptr;
577
 
    }
578
 
    el2->setName(tmpptr, length);
579
 
    headers.push_back(el2);
580
 
    tmpptr += length;
581
 
 
582
 
    // Get the last two pieces of data, which are both AMF encoded
583
 
    // with a type byte.
584
 
    amf::AMF amf;
585
 
    boost::shared_ptr<cygnal::Element> el3 = amf.extractAMF(tmpptr, tmpptr + size);
586
 
    headers.push_back(el3);
587
 
    tmpptr += amf.totalsize();
588
 
    
589
 
    boost::shared_ptr<cygnal::Element> el4 = amf.extractAMF(tmpptr, tmpptr + size);
590
 
    headers.push_back(el4);
591
 
 
592
 
     return headers;
593
 
}
594
 
 
595
 
// format a response to the 'echo' test used for testing Gnash. This
596
 
// is only used for testing by developers. The format appears to be
597
 
// two strings, followed by a double, followed by the "onResult".
598
 
cygnal::Buffer &
599
 
HTTPServer::formatEchoResponse(const std::string &num, cygnal::Element &el)
600
 
{
601
 
//    GNASH_REPORT_FUNCTION;
602
 
    boost::shared_ptr<cygnal::Buffer> data;
603
 
 
604
 
    cygnal::Element nel;
605
 
    if (el.getType() == cygnal::Element::TYPED_OBJECT_AMF0) {
606
 
        nel.makeTypedObject();
607
 
        string name = el.getName();
608
 
        nel.setName(name);
609
 
        if (el.propertySize()) {
610
 
            // FIXME: see about using std::reverse() instead.
611
 
            for (int i=el.propertySize()-1; i>=0; i--) {
612
 
//          for (int i=0 ; i<el.propertySize(); i++) {
613
 
                boost::shared_ptr<cygnal::Element> child = el.getProperty(i);
614
 
                nel.addProperty(child);
615
 
            }
616
 
            data = nel.encode();
617
 
        } else {
618
 
            data = el.encode();
619
 
        }
620
 
    } else {
621
 
        data = el.encode();
622
 
    }
623
 
 
624
 
    return formatEchoResponse(num, data->reference(), data->allocated());
625
 
}
626
 
 
627
 
cygnal::Buffer &
628
 
HTTPServer::formatEchoResponse(const std::string &num, cygnal::Buffer &data)
629
 
{
630
 
//    GNASH_REPORT_FUNCTION;
631
 
    return formatEchoResponse(num, data.reference(), data.allocated());
632
 
}
633
 
 
634
 
cygnal::Buffer &
635
 
HTTPServer::formatEchoResponse(const std::string &num, boost::uint8_t *data, size_t size)
636
 
{
637
 
//    GNASH_REPORT_FUNCTION;
638
 
 
639
 
    //boost::uint8_t *tmpptr  = data;
640
 
    
641
 
    // FIXME: temporary hacks while debugging
642
 
    cygnal::Buffer fixme("00 00 00 00 00 01");
643
 
    cygnal::Buffer fixme2("ff ff ff ff");
644
 
    
645
 
    _buffer = "HTTPServer/1.1 200 OK\r\n";
646
 
    formatContentType(DiskStream::FILETYPE_AMF);
647
 
//    formatContentLength(size);
648
 
    // FIXME: this is a hack ! Calculate a real size!
649
 
    formatContentLength(size+29);
650
 
    
651
 
    // Don't pretend to be the Red5 server
652
 
    formatServer("Cygnal (0.8.6)");
653
 
    
654
 
    // All HTTPServer messages are followed by a blank line.
655
 
    terminateHeader();
656
 
 
657
 
    // Add the binary blob for the header
658
 
    _buffer += fixme;
659
 
 
660
 
    // Make the result response, which is the 2nd data item passed in
661
 
    // the request, a slash followed by a number like "/2".
662
 
    string result = num;
663
 
    result += "/onResult";
664
 
    boost::shared_ptr<cygnal::Buffer> res = amf::AMF::encodeString(result);
665
 
    _buffer.append(res->begin()+1, res->size()-1);
666
 
 
667
 
    // Add the null data item
668
 
    boost::shared_ptr<cygnal::Buffer> null = amf::AMF::encodeString("null");
669
 
    _buffer.append(null->begin()+1, null->size()-1);
670
 
 
671
 
    // Add the other binary blob
672
 
    _buffer += fixme2;
673
 
 
674
 
    cygnal::Element::amf0_type_e type = static_cast<cygnal::Element::amf0_type_e>(*data);
675
 
    if ((type == cygnal::Element::UNSUPPORTED_AMF0)
676
 
        || (type == cygnal::Element::NULL_AMF0)) {
677
 
        _buffer += type;
678
 
        // Red5 returns a NULL object when it's recieved an undefined one in the echo_test
679
 
    } else if (type == cygnal::Element::UNDEFINED_AMF0) {
680
 
        _buffer += cygnal::Element::NULL_AMF0;
681
 
    } else {
682
 
        // Add the AMF data we're echoing back
683
 
        if (size) {
684
 
            _buffer.append(data, size);
685
 
        }
686
 
    }
687
 
    
688
 
    return _buffer;
689
 
}
690
 
#endif
691
 
 
692
 
#if 0
693
 
/// These methods extract data from an RTMPT message. RTMP is an
694
 
/// extension to HTTP that adds commands to manipulate the
695
 
/// connection's persistance.
696
 
//
697
 
/// The URL to be opened has the following form:
698
 
/// http://server/<comand>/[<client>/]<index>
699
 
/// <command>
700
 
///    denotes the RTMPT request type, "OPEN", "SEND", "IDLE", "CLOSE")
701
 
/// <client>
702
 
///    specifies the id of the client that performs the requests
703
 
///    (only sent for established sessions)
704
 
/// <index>
705
 
///    is a consecutive number that seems to be used to detect missing packages
706
 
HTTP::rtmpt_cmd_e
707
 
HTTP::extractRTMPT(boost::uint8_t *data)
708
 
{
709
 
    GNASH_REPORT_FUNCTION;
710
 
 
711
 
    string body = reinterpret_cast<const char *>(data);
712
 
    string tmp, cid, indx;
713
 
    HTTP::rtmpt_cmd_e cmd;
714
 
 
715
 
    // force the case to make comparisons easier
716
 
    std::transform(body.begin(), body.end(), body.begin(), 
717
 
               (int(*)(int)) toupper);
718
 
    string::size_type start, end;
719
 
 
720
 
    // Extract the command first
721
 
    start = body.find("OPEN", 0);
722
 
    if (start != string::npos) {
723
 
        cmd = HTTP::OPEN;
724
 
    }
725
 
    start = body.find("SEND", 0);
726
 
    if (start != string::npos) {
727
 
        cmd = HTTP::SEND;
728
 
    }
729
 
    start = body.find("IDLE", 0);
730
 
    if (start != string::npos) {
731
 
        cmd = HTTP::IDLE;
732
 
    }
733
 
    start = body.find("CLOSE", 0);
734
 
    if (start != string::npos) {
735
 
        cmd = HTTP::CLOSE;
736
 
    }
737
 
 
738
 
    // Extract the optional client id
739
 
    start = body.find("/", start+1);
740
 
    if (start != string::npos) {
741
 
        end = body.find("/", start+1);
742
 
        if (end != string::npos) {
743
 
            indx = body.substr(end, body.size());
744
 
            cid = body.substr(start, (end-start));
745
 
        } else {
746
 
            cid = body.substr(start, body.size());
747
 
        }
748
 
    }
749
 
 
750
 
    _index = strtol(indx.c_str(), NULL, 0);
751
 
    _clientid = strtol(cid.c_str(), NULL, 0);
752
 
    end =  body.find("\r\n", start);
753
 
//     if (end != string::npos) {
754
 
//         cmd = HTTP::CLOSE;
755
 
//     }
756
 
 
757
 
    return cmd;
758
 
}
759
 
 
760
 
#endif
761
 
 
762
 
/// These methods extract data from an RTMPT message. RTMP is an
763
 
/// extension to HTTPServer that adds commands to manipulate the
764
 
/// connection's persistance.
765
 
//
766
 
/// The URL to be opened has the following form:
767
 
/// http://server/<comand>/[<client>/]<index>
768
 
/// <command>
769
 
///    denotes the RTMPT request type, "OPEN", "SEND", "IDLE", "CLOSE")
770
 
/// <client>
771
 
///    specifies the id of the client that performs the requests
772
 
///    (only sent for established sessions)
773
 
/// <index>
774
 
///    is a consecutive number that seems to be used to detect missing packages
775
 
HTTPServer::rtmpt_cmd_e
776
 
HTTPServer::extractRTMPT(boost::uint8_t *data)
777
 
{
778
 
    GNASH_REPORT_FUNCTION;
779
 
 
780
 
    string body = reinterpret_cast<const char *>(data);
781
 
    string tmp, cid, indx;
782
 
    HTTPServer::rtmpt_cmd_e cmd;
783
 
 
784
 
    // force the case to make comparisons easier
785
 
    std::transform(body.begin(), body.end(), body.begin(), 
786
 
               (int(*)(int)) toupper);
787
 
    string::size_type start, end;
788
 
 
789
 
    // Extract the command first
790
 
    start = body.find("OPEN", 0);
791
 
    if (start != string::npos) {
792
 
        cmd = HTTPServer::OPEN;
793
 
    }
794
 
    start = body.find("SEND", 0);
795
 
    if (start != string::npos) {
796
 
        cmd = HTTPServer::SEND;
797
 
    }
798
 
    start = body.find("IDLE", 0);
799
 
    if (start != string::npos) {
800
 
        cmd = HTTPServer::IDLE;
801
 
    }
802
 
    start = body.find("CLOSE", 0);
803
 
    if (start != string::npos) {
804
 
        cmd = HTTPServer::CLOSE;
805
 
    }
806
 
 
807
 
    // Extract the optional client id
808
 
    start = body.find("/", start+1);
809
 
    if (start != string::npos) {
810
 
        end = body.find("/", start+1);
811
 
        if (end != string::npos) {
812
 
            indx = body.substr(end, body.size());
813
 
            cid = body.substr(start, (end-start));
814
 
        } else {
815
 
            cid = body.substr(start, body.size());
816
 
        }
817
 
    }
818
 
 
819
 
    _index = strtol(indx.c_str(), NULL, 0);
820
 
    _clientid = strtol(cid.c_str(), NULL, 0);
821
 
    end =  body.find("\r\n", start);
822
 
//     if (end != string::npos) {
823
 
//         cmd = HTTPServer::CLOSE;
824
 
//     }
825
 
 
826
 
    return cmd;
827
 
}
828
 
 
829
 
#if 0
830
 
HTTPServer::http_method_e
831
 
HTTPServer::extractCommand(boost::uint8_t *data)
832
 
{
833
 
    GNASH_REPORT_FUNCTION;
834
 
 
835
 
//    string body = reinterpret_cast<const char *>(data);
836
 
    HTTPServer::http_method_e cmd = HTTP::HTTP_NONE;
837
 
 
838
 
    // force the case to make comparisons easier
839
 
//     std::transform(body.begin(), body.end(), body.begin(), 
840
 
//                (int(*)(int)) toupper);
841
 
 
842
 
    // Extract the command
843
 
    if (memcmp(data, "GET", 3) == 0) {
844
 
        cmd = HTTP::HTTP_GET;
845
 
    } else if (memcmp(data, "POST", 4) == 0) {
846
 
        cmd = HTTP::HTTP_POST;
847
 
    } else if (memcmp(data, "HEAD", 4) == 0) {
848
 
        cmd = HTTP::HTTP_HEAD;
849
 
    } else if (memcmp(data, "CONNECT", 7) == 0) {
850
 
        cmd = HTTP::HTTP_CONNECT;
851
 
    } else if (memcmp(data, "TRACE", 5) == 0) {
852
 
        cmd = HTTP::HTTP_TRACE;
853
 
    } else if (memcmp(data, "PUT", 3) == 0) {
854
 
        cmd = HTTP::HTTP_PUT;
855
 
    } else if (memcmp(data, "OPTIONS", 4) == 0) {
856
 
        cmd = HTTP::HTTP_OPTIONS;
857
 
    } else if (memcmp(data, "DELETE", 4) == 0) {
858
 
        cmd = HTTP::HTTP_DELETE;
859
 
    }
860
 
 
861
 
    // For valid requests, the second argument, delimited by spaces
862
 
    // is the filespec of the file being requested or transmitted.
863
 
    if (cmd != HTTP::HTTP_NONE) {
864
 
        boost::uint8_t *start = std::find(data, data+7, ' ') + 1;
865
 
        boost::uint8_t *end   = std::find(start + 2, data+PATH_MAX, ' ');
866
 
        boost::uint8_t *params = std::find(start, end, '?');
867
 
        if (params != end) {
868
 
            _params = std::string(params+1, end);
869
 
            _filespec = std::string(start, params);
870
 
            log_debug("Parameters for file: \"%s\"", _params);
871
 
        } else {
872
 
            // This is fine as long as end is within the buffer.
873
 
            _filespec = std::string(start, end);
874
 
        }
875
 
        // log_debug("Requesting file: \"%s\"", _filespec);
876
 
 
877
 
        // The third field is always the HTTP version
878
 
        // The version is the last field and is the protocol name
879
 
        // followed by a slash, and the version number. Note that
880
 
        // the version is not a double, even though it has a dot
881
 
        // in it. It's actually two separate integers.
882
 
        _version.major = *(end+6) - '0';
883
 
        _version.minor = *(end+8) - '0';
884
 
        // log_debug (_("Version: %d.%d"), _version.major, _version.minor);
885
 
    }
886
 
 
887
 
    return cmd;
888
 
}
889
 
 
890
 
boost::uint8_t *
891
 
HTTPServer::processHeaderFields(cygnal::Buffer &buf)
892
 
{
893
 
  //    GNASH_REPORT_FUNCTION;
894
 
    string head(reinterpret_cast<const char *>(buf.reference()));
895
 
 
896
 
    // The end of the header block is always followed by a blank line
897
 
    string::size_type end = head.find("\r\n\r\n", 0);
898
 
//    head.erase(end, buf.size()-end);
899
 
    Tok t(head, Sep("\r\n"));
900
 
    for (Tok::iterator i = t.begin(); i != t.end(); ++i) {
901
 
        string::size_type pos = i->find(":", 0);
902
 
        if (pos != string::npos) {
903
 
            string name = i->substr(0, pos);
904
 
            string value = i->substr(pos+2, i->size());
905
 
            std::transform(name.begin(), name.end(), name.begin(), 
906
 
                           (int(*)(int)) tolower);
907
 
            std::transform(value.begin(), value.end(), value.begin(), 
908
 
                           (int(*)(int)) tolower);
909
 
            _fields[name] = value;
910
 
            if (name == "keep-alive") {
911
 
                _keepalive = true;
912
 
                if ((value != "on") && (value != "off")) {
913
 
                    _max_requests = strtol(value.c_str(), NULL, 0);
914
 
                    log_debug("Setting Max Requests for Keep-Alive to %d", _max_requests);
915
 
                }
916
 
            }
917
 
            if (name == "connection") {
918
 
                if (value.find("keep-alive", 0) != string::npos) {
919
 
                    _keepalive = true;
920
 
                }
921
 
            }
922
 
            if (name == "content-length") {
923
 
                _filesize = strtol(value.c_str(), NULL, 0);
924
 
                log_debug("Setting Content Length to %d", _filesize);
925
 
            }
926
 
            if (name == "content-type") {
927
 
                // This is the type used by flash when sending a AMF data via POST
928
 
                if (value == "application/x-amf") {
929
 
//                  log_debug("Got AMF data in the POST request!");
930
 
                    _filetype = DiskStream::FILETYPE_AMF;
931
 
                }
932
 
                // This is the type used by wget when sending a file via POST
933
 
                if (value == "application/x-www-form-urlencoded") {
934
 
//                  log_debug("Got file data in the POST request");
935
 
                    _filetype = DiskStream::FILETYPE_ENCODED;
936
 
                }
937
 
                log_debug("Setting Content Type to %d", _filetype);
938
 
            }
939
 
            
940
 
//          cerr << "FIXME: " << (void *)i << " : " << dec <<  end << endl;
941
 
        } else {
942
 
            const boost::uint8_t *cmd = reinterpret_cast<const boost::uint8_t *>(i->c_str());
943
 
            if (extractCommand(const_cast<boost::uint8_t *>(cmd)) == HTTP::HTTP_NONE) {
944
 
                break;
945
 
#if 1
946
 
            } else {
947
 
                log_debug("Got a request, parsing \"%s\"", *i);
948
 
                string::size_type start = i->find(" ");
949
 
                string::size_type params = i->find("?");
950
 
                string::size_type pos = i->find("HTTP/");
951
 
                if (pos != string::npos) {
952
 
                    // The version is the last field and is the protocol name
953
 
                    // followed by a slash, and the version number. Note that
954
 
                    // the version is not a double, even though it has a dot
955
 
                    // in it. It's actually two separate integers.
956
 
                    _version.major = i->at(pos+5) - '0';
957
 
                    _version.minor = i->at(pos+7) - '0';
958
 
                    // log_debug (_("Version: %d.%d"), _version.major, _version.minor);
959
 
                    // the filespec in the request is the middle field, deliminated
960
 
                    // by a space on each end.
961
 
                    if (params != string::npos) {
962
 
                        _params = i->substr(params+1, end);
963
 
                        _filespec = i->substr(start+1, params);
964
 
                        log_debug("Parameters for file: \"%s\"", _params);
965
 
                    } else {
966
 
                        _filespec = i->substr(start+1, pos-start-2);
967
 
                    }
968
 
                    // log_debug("Requesting file: \"%s\"", _filespec);
969
 
 
970
 
                    // HTTP 1.1 enables persistant network connections
971
 
                    // by default.
972
 
                    if (_version.minor > 0) {
973
 
                        log_debug("Enabling Keep Alive by default for HTTP > 1.0");
974
 
                        _keepalive = true;
975
 
                    }
976
 
                }
977
 
            }
978
 
#endif
979
 
        }
980
 
    }
981
 
    
982
 
    return buf.reference() + end + 4;
983
 
}
984
 
#endif
985
 
 
986
 
void
987
 
HTTPServer::dump()
988
 
{
989
 
//    GNASH_REPORT_FUNCTION;
990
 
    if (_diskstream) {
991
 
        _diskstream->dump();
992
 
    }
993
 
}
994
 
    
995
 
bool
996
 
HTTPServer::http_handler(Handler *hand, int netfd, cygnal::Buffer *buf)
997
 
{
998
 
    GNASH_REPORT_FUNCTION;
999
 
 
1000
 
    // Handler *hand = reinterpret_cast<Handler *>(args->handler);
1001
 
    // cygnal::Buffer *buf = args->buffer;
1002
 
    // boost::shared_ptr<HTTPServer> www(new HTTPServer); // = hand->getHTTPHandler    (args->netfd);
1003
 
    
1004
 
    string url, parameters;
1005
 
    // by default, only look once unless changed later
1006
 
    // www->setDocRoot(crcfile.getDocumentRoot());
1007
 
    // log_network("Docroot for HTTP files is %s", crcfile.getDocumentRoot());
1008
 
    log_network("Processing HTTP data for fd #%d", netfd);
1009
 
    
1010
 
    // Wait for data, and when we get it, process it.
1011
 
#ifdef USE_STATISTICS
1012
 
    struct timespec start;
1013
 
    clock_gettime (CLOCK_REALTIME, &start);
1014
 
#endif
1015
 
 
1016
 
    if (buf) {
1017
 
        log_network("FIXME: Existing data in packet!");
1018
 
    } else {
1019
 
        log_network("FIXME: No existing data in packet!");    
1020
 
        // See if we have any messages waiting
1021
 
        if (recvMsg(netfd) == 0) {
1022
 
            log_debug("Net HTTP server failed to read from fd #%d...", netfd);
1023
 
            return false;
1024
 
        }
1025
 
    }
1026
 
    
1027
 
    // Process incoming messages
1028
 
    HTTP::http_method_e cmd = processClientRequest(hand, netfd, buf);    
1029
 
    if (cmd != HTTP::HTTP_GET) {
1030
 
        log_debug("No active DiskStreams for fd #%d: %s...", netfd, _filespec);
1031
 
    } else {
1032
 
        if (_diskstream) {
1033
 
            log_debug("Found active DiskStream! for fd #%d: %s", netfd, _filespec);
1034
 
            hand->setDiskStream(netfd, _diskstream);
1035
 
            cache.addFile(_filespec, _diskstream);
1036
 
// Send the first chunk of the file to the client.
1037
 
            // log_network("Sending first chunk of %s", _filespec);
1038
 
            _diskstream->play(netfd, false);
1039
 
        }
1040
 
    }
1041
 
    
1042
 
//      www->dump();
1043
 
    if ((getField("content-type") == "application/x-amf")
1044
 
        && (getField("content-type") == "application/x-amf")
1045
 
        && (getFilespec() == "/echo/gateway")) {
1046
 
        cerr << "GOT A GATEWAY REQUEST" << endl;
1047
 
    }
1048
 
    
1049
 
#if 0
1050
 
    string response = cache.findResponse(filestream->getFilespec());
1051
 
    if (response.empty()) {
1052
 
        cerr << "FIXME no cache hit for: " << www.getFilespec() << endl;
1053
 
//          www.clearHeader();
1054
 
//          cygnal::Buffer &ss = www.formatHeader(filestream->getFileSize(), HTTP::LIFE_IS_GOOD);
1055
 
//          www.writeNet(args->netfd, (boost::uint8_t *)www.getHeader().c_str(), www.getHeader().size());
1056
 
//          cache.addResponse(www.getFilespec(), www.getHeader());
1057
 
    } else {
1058
 
        cerr << "FIXME cache hit on: " << www.getFilespec() << endl;
1059
 
        www.writeNet(args->netfd, (boost::uint8_t *)response.c_str(), response.size());
1060
 
    }   
1061
 
#endif
1062
 
    
1063
 
    // Unless the Keep-Alive flag is set, this isn't a persisant network
1064
 
    // connection.
1065
 
    if (!keepAlive()) {
1066
 
        log_debug("Keep-Alive is off", keepAlive());
1067
 
    } else {
1068
 
        log_debug("Keep-Alive is on", keepAlive());
1069
 
    }
1070
 
#ifdef USE_STATISTICS
1071
 
    struct timespec end;
1072
 
    clock_gettime (CLOCK_REALTIME, &end);
1073
 
    log_debug("Processing time for GET request was %f seconds",
1074
 
              static_cast<float>(((end.tv_sec - start.tv_sec) +
1075
 
                                  ((end.tv_nsec - start.tv_nsec)/1e9))));
1076
 
#endif
1077
 
    
1078
 
    return keepAlive();
1079
 
    
1080
 
} // end of http_handler
1081
 
    
1082
 
} // end of gnash namespace
1083
 
 
1084
 
 
1085
 
// local Variables:
1086
 
// mode: C++
1087
 
// indent-tabs-mode: t
1088
 
// End: