Include program counter on action limit notification log
[gnash.git] / cygnal / handler.cpp
blobf5d3b246b107a8be3bb78b8658702b710770d63d
1 //
2 // Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 #ifdef HAVE_CONFIG_H
20 #include "gnashconfig.h"
21 #endif
23 #include <boost/thread/thread.hpp>
24 #include <boost/thread/mutex.hpp>
25 #include <boost/shared_ptr.hpp>
26 #include <boost/shared_array.hpp>
27 #include <boost/scoped_ptr.hpp>
28 #include <boost/bind.hpp>
29 #include <algorithm>
30 #include <string>
31 #include <deque>
32 #include <list>
33 #include <map>
34 #include <vector>
35 #if defined(WIN32) || defined(_WIN32)
36 # define LIBLTDL_DLL_IMPORT 1
37 #endif
38 #ifdef HAVE_DLFCN_H
39 # include <dlfcn.h>
40 #endif
41 #ifdef HAVE_LIBGEN_H
42 # include <libgen.h>
43 #endif
45 #include "ltdl.h"
46 #include "log.h"
47 #include "network.h"
48 #include "buffer.h"
49 #include "utility.h"
50 #include "dsodefs.h" //For DSOEXPORT.
51 #include "URL.h"
52 #include "handler.h"
53 #include "diskstream.h"
54 #include "rtmp.h"
55 #include "http.h"
56 #include "crc.h"
57 #include "flv.h"
59 #include "rtmp_server.h"
60 #include "http_server.h"
62 using namespace gnash;
63 using namespace std;
64 using namespace boost;
66 namespace cygnal
69 map<int, Handler *> DSOEXPORT handlers;
71 // The user config for Cygnal is loaded and parsed here:
72 static CRcInitFile& crcfile = CRcInitFile::getDefaultInstance();
74 Handler::Handler()
75 :_streams(1), // note that stream 0 is reserved by the system.
76 // _diskstreams(new gnash::DiskStream[STREAMS_BLOCK]),
77 _in_fd(0)
79 // GNASH_REPORT_FUNCTION;
80 // reserve memory for the vector as it makes vector operations
81 // must faster.
84 Handler::~Handler()
86 // GNASH_REPORT_FUNCTION;
89 bool
90 Handler::sync(int /* in_fd */)
92 // GNASH_REPORT_FUNCTION;
94 return false;
97 size_t
98 Handler::addClient(int fd, Network::protocols_supported_e proto)
100 // GNASH_REPORT_FUNCTION;
102 boost::mutex::scoped_lock lock(_mutex);
104 log_debug("Adding %d to the client array.", fd);
105 switch (proto) {
106 case Network::NONE:
107 break;
108 case Network::HTTP:
110 boost::shared_ptr<HTTPServer> http(new HTTPServer);
111 _http[fd] = http;
112 break;
114 case Network::HTTPS:
115 break;
116 case Network::RTMP:
118 boost::shared_ptr<RTMPServer> rtmp(new RTMPServer);
119 _rtmp[fd] = rtmp;
120 break;
122 case Network::RTMPT:
123 case Network::RTMPTS:
124 case Network::RTMPE:
125 case Network::RTMPS:
126 case Network::DTN:
127 default:
128 log_unimpl("Protocol %d for Handler::AddClient()", proto);
129 break;
132 _clients.push_back(fd);
133 _protocol[fd] = proto;
135 return _clients.size();
138 // Parse the first nessages when starting a new message handler,
139 // which is used to determine the name of the resource to
140 // initialize, or load from the cache.
141 cygnal::Buffer *
142 Handler::parseFirstRequest(int fd, gnash::Network::protocols_supported_e proto)
144 GNASH_REPORT_FUNCTION;
145 string key;
146 Network net;
147 cygnal::Buffer *buf = 0;
148 boost::mutex::scoped_lock lock(_mutex);
150 switch (proto) {
151 case Network::NONE:
152 break;
153 case Network::HTTP:
155 #if 0
156 int ret = _http[fd]->readNet(fd, buf);
157 if (ret) {
158 _http[fd]->processHeaderFields(buf);
159 string hostname, path;
160 string::size_type pos = _http[fd]->getField("host").find(":", 0);
161 if (pos != string::npos) {
162 hostname += _http[fd]->getField("host").substr(0, pos);
163 } else {
164 hostname += "localhost";
166 path = _http[fd]->getFilespec();
167 key = hostname + path;
168 log_debug("HTTP key is: %s", key);
169 _keys[fd] = key;
170 } else {
171 log_error("HTTP key couldn't be read!");
173 #else
174 HTTPServer http;
175 size_t bytes = http.sniffBytesReady(fd);
176 if (bytes) {
177 buf = new cygnal::Buffer(bytes);
178 } else {
179 return 0;
181 int ret = http.readNet(fd, buf);
182 if (ret) {
183 http.processHeaderFields(buf);
184 string hostname, path;
185 string::size_type pos = http.getField("host").find(":", 0);
186 if (pos != string::npos) {
187 hostname += http.getField("host").substr(0, pos);
188 } else {
189 hostname += "localhost";
191 path = http.getFilespec();
192 key = hostname + path;
193 log_debug("HTTP key is: %s", key);
194 _keys[fd] = key;
195 } else {
196 log_error("HTTP key couldn't be read!");
198 #endif
199 break;
201 case Network::HTTPS:
202 break;
203 case Network::RTMP:
205 // _rtmp[fd]->recvMsg(fd);
206 break;
208 case Network::RTMPT:
209 case Network::RTMPTS:
210 case Network::RTMPE:
211 case Network::RTMPS:
212 case Network::DTN:
213 default:
214 log_error("FD #%d has no protocol handler registered", fd);
215 break;
218 return buf;
222 Handler::recvMsg(int fd)
224 // GNASH_REPORT_FUNCTION;
225 boost::mutex::scoped_lock lock(_mutex);
227 switch (_protocol[fd]) {
228 case Network::NONE:
229 break;
230 case Network::HTTP:
232 return _http[fd]->recvMsg(fd);
233 break;
235 case Network::HTTPS:
236 break;
237 case Network::RTMP:
238 case Network::RTMPT:
239 case Network::RTMPTS:
240 case Network::RTMPE:
241 case Network::RTMPS:
242 case Network::DTN:
243 default:
244 log_error("FD #%d has no protocol handler registered", fd);
245 break;
249 void
250 Handler::removeClient(int x)
252 // GNASH_REPORT_FUNCTION;
254 boost::mutex::scoped_lock lock(_mutex);
256 vector<int>::iterator it;
257 for (it = _clients.begin(); it < _clients.end(); ++it) {
258 if (*it == x) {
259 log_debug("Removing %d from the client array.", *it);
260 _clients.erase(it);
265 void
266 Handler::setPlugin(boost::shared_ptr<Handler::cygnal_init_t> &/* init */)
268 // GNASH_REPORT_FUNCTION;
269 // _plugin.reset(init.get());
272 void
273 Handler::setPlugin(Handler::cygnal_io_read_t /* read_ptr */, Handler::cygnal_io_write_t /* write_ptr */)
275 // GNASH_REPORT_FUNCTION;
277 _plugin.reset(new Handler::cygnal_init_t);
280 boost::shared_ptr<Handler::cygnal_init_t>
281 Handler::initModule(const std::string& str)
283 // GNASH_REPORT_FUNCTION;
285 if (str.empty()) {
286 return _plugin;
289 string module = str;
290 if (module[0] == '/') {
291 module.erase(0,1);
294 SharedLib *sl;
295 string symbol(module);
297 _pluginsdir = PLUGINSDIR;
298 log_security(_("Initializing module: \"%s\" from %s"), symbol, _pluginsdir);
300 // Update the list of loaded plugins so we only load them once.
301 if (_plugins[module] == 0) {
302 sl = new SharedLib(module, "CYGNAL_PLUGINS");
303 lt_dlsetsearchpath(_pluginsdir.c_str());
304 sl->openLib();
305 _plugins[module] = sl;
306 } else {
307 sl = _plugins[module];
310 _plugin.reset(new Handler::cygnal_init_t);
312 symbol = module;
313 symbol.append("_init_func");
314 Handler::cygnal_io_init_t init_symptr = reinterpret_cast<Handler::cygnal_io_init_t>
315 (sl->getInitEntry(symbol));
316 if (!init_symptr) {
317 log_network(_("No %s symbol in plugin"), symbol);
318 } else {
319 boost::shared_ptr<cygnal_init_t> info = init_symptr(_netconnect);
320 log_network("Initialized Plugin: \"%s\": %s", info->version,
321 info->description);
324 // Look for the "module"_read_init function we'll use to get data
325 // from the cgi-bin as a dynamically loadable plugin.
326 symbol = module;
327 symbol.append("_read_func");
329 Handler::cygnal_io_read_t read_symptr = reinterpret_cast<Handler::cygnal_io_read_t>
330 (sl->getInitEntry(symbol));
332 if (!read_symptr) {
333 log_error(_("Couldn't get %s symbol"), symbol);
334 _plugin.reset();
335 return _plugin;
338 _plugin->read_func = read_symptr;
340 // Look for the "module"_write_init function we'll use to send data
341 // to the cgi-bin as a dynamically loadable plugin.
342 symbol = module;
343 symbol.append("_write_func");
344 Handler::cygnal_io_write_t write_symptr = reinterpret_cast<Handler::cygnal_io_write_t>
345 (sl->getInitEntry(symbol));
347 if (!write_symptr) {
348 log_error(_("Couldn't get %s symbol"), symbol);
349 _plugin.reset();
350 return _plugin;
353 _plugin->write_func = write_symptr;
355 return _plugin;
358 size_t
359 Handler::writeToPlugin(boost::uint8_t *data, size_t size)
361 // GNASH_REPORT_FUNCTION;
362 size_t ret = 0;
363 if (_plugin) {
364 ret = _plugin->write_func(data, size);
367 return ret;
370 boost::shared_ptr<cygnal::Buffer>
371 Handler::readFromPlugin()
373 // GNASH_REPORT_FUNCTION;
375 boost::shared_ptr<cygnal::Buffer> buf;
376 if (_plugin) {
377 buf = _plugin->read_func();
380 return buf;
383 bool
384 Handler::initialized()
386 // GNASH_REPORT_FUNCTION;
387 if (_files.empty()
388 && (_clients.size() == 1)
389 && !_local
390 && _remote.empty()
391 && !_plugin) {
392 return false;
395 return true;
398 // Find a stream in the vector or Disk Streams
399 boost::shared_ptr<gnash::DiskStream>
400 Handler::findStream(const std::string &filespec)
402 // GNASH_REPORT_FUNCTION;
404 for (int i; i < _streams; i++) {
405 if (_diskstreams[i]->getFilespec() == filespec) {
406 return _diskstreams[i];
410 return _diskstreams[0];
413 // Create a new DiskStream
414 double
415 Handler::createStream(double /* transid */)
417 GNASH_REPORT_FUNCTION;
419 _diskstreams[_streams]->setState(DiskStream::CREATED);
421 return _streams;
424 // Create a new DiskStream
425 double
426 Handler::createStream(double /* transid */, const std::string &filespec)
428 GNASH_REPORT_FUNCTION;
430 if (filespec.empty()) {
431 return -1;
434 _diskstreams[_streams]->setState(DiskStream::CREATED);
435 _diskstreams[_streams]->setFilespec(filespec);
437 return _streams;
440 bool
441 Handler::playStream()
443 GNASH_REPORT_FUNCTION;
445 // _diskstreams[int(streamid)]->setState(DiskStream::PLAY);
447 return false;
450 bool
451 Handler::playStream(const std::string &filespec)
453 GNASH_REPORT_FUNCTION;
455 boost::shared_ptr<gnash::DiskStream> ds = _diskstreams[_streams];
457 string fullpath = crcfile.getDocumentRoot();
458 fullpath += "/";
459 fullpath += filespec;
460 log_debug("FILENAME: %s", fullpath);
462 // gnash::DiskStream &ds = findStream(filespec);
463 if (ds->getState() == DiskStream::CREATED) {
464 if (ds->open(fullpath)) {
465 ds->loadToMem(0); // FIXME: load only part of the whole file for now
466 ds->setState(DiskStream::PLAY);
467 return true;
471 return false;
474 // Publish a live stream
476 Handler::publishStream()
478 GNASH_REPORT_FUNCTION;
480 return publishStream("", Handler::LIVE);
484 Handler::publishStream(const std::string &/*filespec */, Handler::pub_stream_e /* op
487 GNASH_REPORT_FUNCTION;
489 // _diskstreams[int(streamid)]->setState(DiskStream::PUBLISH);
491 return -1;
494 // Seek within the RTMP stream
496 Handler::seekStream()
498 GNASH_REPORT_FUNCTION;
499 // _diskstreams[int(streamid)]->setState(DiskStream::SEEK);
501 return -1;
505 Handler::seekStream(int /* offset */)
507 GNASH_REPORT_FUNCTION;
508 // _diskstreams[int(streamid)]->setState(DiskStream::SEEK);
510 return -1;
513 // Pause the RTMP stream
515 Handler::pauseStream(double streamid)
517 GNASH_REPORT_FUNCTION;
519 _diskstreams[int(streamid)]->setState(DiskStream::PAUSE);
521 return -1;
524 // Pause the RTMP stream
526 Handler::togglePause(double streamid)
528 GNASH_REPORT_FUNCTION;
530 if (_diskstreams[int(streamid)]->getState() == DiskStream::PAUSE) {
531 _diskstreams[int(streamid)]->setState(DiskStream::PLAY);
532 } if (_diskstreams[int(streamid)]->getState() == DiskStream::PLAY) {
533 _diskstreams[int(streamid)]->setState(DiskStream::PAUSE);
536 return -1;
539 // Resume the paused RTMP stream
540 double
541 Handler::resumeStream(double streamid)
543 GNASH_REPORT_FUNCTION;
545 togglePause(streamid);
547 return -1;
550 // Close the RTMP stream
551 double
552 Handler::closeStream(double streamid)
554 GNASH_REPORT_FUNCTION;
556 _diskstreams[int(streamid)]->setState(DiskStream::CLOSED);
558 return -1;
561 // Delete the RTMP stream
562 double
563 Handler::deleteStream(double streamid)
565 GNASH_REPORT_FUNCTION;
567 _diskstreams[int(streamid)]->setState(DiskStream::NO_STATE);
569 _streams++;
571 return _streams;
574 // Dump internal data.
575 void
576 Handler::dump()
578 const char *proto_str[] = {
579 "NONE",
580 "HTTP",
581 "HTTPS",
582 "RTMP",
583 "RTMPT",
584 "RTMPTS",
585 "RTMPE",
586 "RTMPS",
587 "DTN"
590 // GNASH_REPORT_FUNCTION;
591 cerr << "Currently there are " <<_clients.size() << " clients connected."
592 << endl;
593 for (size_t i = 0; i < _clients.size(); i++) {
594 cerr << "Client on fd #" << _clients[i] << " is using "
595 << proto_str[_protocol[i]] << endl;
598 cerr << "Currently there are " << dec <<_diskstreams.size() << " DiskStreams."
599 << endl;
600 map<int, boost::shared_ptr<DiskStream> >::iterator it;
601 for (it = _diskstreams.begin(); it != _diskstreams.end(); ++it) {
602 if (it->second) {
603 cerr << "DiskStream for fd #" << dec << it->first << endl;
604 it->second->dump();
610 } // end of gnash namespace
612 // local Variables:
613 // mode: C++
614 // indent-tabs-mode: t
615 // End: