Update with current status
[gnash.git] / cygnal / handler.cpp
blobd1abec76713a257771db21ec4b97ffa8f0ade635
1 //
2 // Copyright (C) 2008, 2009, 2010, 2011, 2012 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 <mutex>
24 #include <memory>
25 #include <functional>
26 #include <algorithm>
27 #include <string>
28 #include <deque>
29 #include <list>
30 #include <map>
31 #include <vector>
32 #if defined(WIN32) || defined(_WIN32)
33 # define LIBLTDL_DLL_IMPORT 1
34 #endif
35 #ifdef HAVE_DLFCN_H
36 # include <dlfcn.h>
37 #endif
38 #ifdef HAVE_LIBGEN_H
39 # include <libgen.h>
40 #endif
42 #include "ltdl.h"
43 #include "log.h"
44 #include "network.h"
45 #include "buffer.h"
46 #include "utility.h"
47 #include "dsodefs.h" //For DSOEXPORT.
48 #include "URL.h"
49 #include "handler.h"
50 #include "diskstream.h"
51 #include "rtmp.h"
52 #include "http.h"
53 #include "crc.h"
54 #include "flv.h"
56 #include "rtmp_server.h"
57 #include "http_server.h"
59 using namespace gnash;
60 using namespace std;
61 using namespace boost;
63 namespace cygnal
66 map<int, Handler *> DSOEXPORT handlers;
68 // The user config for Cygnal is loaded and parsed here:
69 static CRcInitFile& crcfile = CRcInitFile::getDefaultInstance();
71 Handler::Handler()
72 :_streams(1), // note that stream 0 is reserved by the system.
73 // _diskstreams(new gnash::DiskStream[STREAMS_BLOCK]),
74 _in_fd(0)
76 // GNASH_REPORT_FUNCTION;
77 // reserve memory for the vector as it makes vector operations
78 // must faster.
81 Handler::~Handler()
83 // GNASH_REPORT_FUNCTION;
86 bool
87 Handler::sync(int /* in_fd */)
89 // GNASH_REPORT_FUNCTION;
91 return false;
94 size_t
95 Handler::addClient(int fd, Network::protocols_supported_e proto)
97 // GNASH_REPORT_FUNCTION;
99 std::lock_guard<std::mutex> lock(_mutex);
101 log_debug("Adding %d to the client array.", fd);
102 switch (proto) {
103 case Network::NONE:
104 break;
105 case Network::HTTP:
107 std::shared_ptr<HTTPServer> http(new HTTPServer);
108 _http[fd] = http;
109 break;
111 case Network::HTTPS:
112 break;
113 case Network::RTMP:
115 std::shared_ptr<RTMPServer> rtmp(new RTMPServer);
116 _rtmp[fd] = rtmp;
117 break;
119 case Network::RTMPT:
120 case Network::RTMPTS:
121 case Network::RTMPE:
122 case Network::RTMPS:
123 case Network::DTN:
124 default:
125 log_unimpl(_("Protocol %d for Handler::AddClient()"), proto);
126 break;
129 _clients.push_back(fd);
130 _protocol[fd] = proto;
132 return _clients.size();
135 // Parse the first nessages when starting a new message handler,
136 // which is used to determine the name of the resource to
137 // initialize, or load from the cache.
138 cygnal::Buffer *
139 Handler::parseFirstRequest(int fd, gnash::Network::protocols_supported_e proto)
141 GNASH_REPORT_FUNCTION;
142 string key;
143 Network net;
144 cygnal::Buffer *buf = 0;
145 std::lock_guard<std::mutex> lock(_mutex);
147 switch (proto) {
148 case Network::NONE:
149 break;
150 case Network::HTTP:
152 #if 0
153 int ret = _http[fd]->readNet(fd, buf);
154 if (ret) {
155 _http[fd]->processHeaderFields(buf);
156 string hostname, path;
157 string::size_type pos = _http[fd]->getField("host").find(":", 0);
158 if (pos != string::npos) {
159 hostname += _http[fd]->getField("host").substr(0, pos);
160 } else {
161 hostname += "localhost";
163 path = _http[fd]->getFilespec();
164 key = hostname + path;
165 log_debug("HTTP key is: %s", key);
166 _keys[fd] = key;
167 } else {
168 log_error(_("HTTP key couldn't be read!"));
170 #else
171 HTTPServer http;
172 size_t bytes = http.sniffBytesReady(fd);
173 if (bytes) {
174 buf = new cygnal::Buffer(bytes);
175 } else {
176 return 0;
178 int ret = http.readNet(fd, buf);
179 if (ret) {
180 http.processHeaderFields(buf);
181 string hostname, path;
182 string::size_type pos = http.getField("host").find(":", 0);
183 if (pos != string::npos) {
184 hostname += http.getField("host").substr(0, pos);
185 } else {
186 hostname += "localhost";
188 path = http.getFilespec();
189 key = hostname + path;
190 log_debug("HTTP key is: %s", key);
191 _keys[fd] = key;
192 } else {
193 log_error(_("HTTP key couldn't be read!"));
195 #endif
196 break;
198 case Network::HTTPS:
199 break;
200 case Network::RTMP:
202 // _rtmp[fd]->recvMsg(fd);
203 break;
205 case Network::RTMPT:
206 case Network::RTMPTS:
207 case Network::RTMPE:
208 case Network::RTMPS:
209 case Network::DTN:
210 default:
211 log_error(_("FD #%d has no protocol handler registered"), fd);
212 break;
215 return buf;
219 Handler::recvMsg(int fd)
221 // GNASH_REPORT_FUNCTION;
222 std::lock_guard<std::mutex> lock(_mutex);
224 switch (_protocol[fd]) {
225 case Network::NONE:
226 break;
227 case Network::HTTP:
229 return _http[fd]->recvMsg(fd);
230 break;
232 case Network::HTTPS:
233 break;
234 case Network::RTMP:
235 case Network::RTMPT:
236 case Network::RTMPTS:
237 case Network::RTMPE:
238 case Network::RTMPS:
239 case Network::DTN:
240 default:
241 log_error(_("FD #%d has no protocol handler registered"), fd);
242 break;
245 return 0;
248 void
249 Handler::removeClient(int x)
251 // GNASH_REPORT_FUNCTION;
253 std::lock_guard<std::mutex> lock(_mutex);
255 vector<int>::iterator it;
256 for (it = _clients.begin(); it < _clients.end(); ++it) {
257 if (*it == x) {
258 log_debug("Removing %d from the client array.", *it);
259 _clients.erase(it);
264 void
265 Handler::setPlugin(std::shared_ptr<Handler::cygnal_init_t> &/* init */)
267 // GNASH_REPORT_FUNCTION;
268 // _plugin.reset(init.get());
271 void
272 Handler::setPlugin(Handler::cygnal_io_read_t /* read_ptr */, Handler::cygnal_io_write_t /* write_ptr */)
274 // GNASH_REPORT_FUNCTION;
276 _plugin.reset(new Handler::cygnal_init_t);
279 std::shared_ptr<Handler::cygnal_init_t>
280 Handler::initModule(const std::string& str)
282 // GNASH_REPORT_FUNCTION;
284 if (str.empty()) {
285 return _plugin;
288 string module = str;
289 if (module[0] == '/') {
290 module.erase(0,1);
293 SharedLib *sl;
294 string symbol(module);
296 _pluginsdir = PLUGINSDIR;
297 log_security(_("Initializing module: \"%s\" from %s"), symbol, _pluginsdir);
299 // Update the list of loaded plugins so we only load them once.
300 if (_plugins[module] == 0) {
301 sl = new SharedLib(module);
302 lt_dlsetsearchpath(_pluginsdir.c_str());
303 sl->openLib();
304 _plugins[module] = sl;
305 } else {
306 sl = _plugins[module];
309 _plugin.reset(new Handler::cygnal_init_t);
311 symbol = module;
312 symbol.append("_init_func");
313 Handler::cygnal_io_init_t init_symptr = reinterpret_cast<Handler::cygnal_io_init_t>
314 (sl->getInitEntry(symbol));
315 if (!init_symptr) {
316 log_network(_("No %s symbol in plugin"), symbol);
317 } else {
318 std::shared_ptr<cygnal_init_t> info = init_symptr(_netconnect);
319 log_network(_("Initialized Plugin: \"%s\": %s"), info->version,
320 info->description);
323 // Look for the "module"_read_init function we'll use to get data
324 // from the cgi-bin as a dynamically loadable plugin.
325 symbol = module;
326 symbol.append("_read_func");
328 Handler::cygnal_io_read_t read_symptr = reinterpret_cast<Handler::cygnal_io_read_t>
329 (sl->getInitEntry(symbol));
331 if (!read_symptr) {
332 log_error(_("Couldn't get %s symbol"), symbol);
333 _plugin.reset();
334 return _plugin;
337 _plugin->read_func = read_symptr;
339 // Look for the "module"_write_init function we'll use to send data
340 // to the cgi-bin as a dynamically loadable plugin.
341 symbol = module;
342 symbol.append("_write_func");
343 Handler::cygnal_io_write_t write_symptr = reinterpret_cast<Handler::cygnal_io_write_t>
344 (sl->getInitEntry(symbol));
346 if (!write_symptr) {
347 log_error(_("Couldn't get %s symbol"), symbol);
348 _plugin.reset();
349 return _plugin;
352 _plugin->write_func = write_symptr;
354 return _plugin;
357 size_t
358 Handler::writeToPlugin(std::uint8_t *data, size_t size)
360 // GNASH_REPORT_FUNCTION;
361 size_t ret = 0;
362 if (_plugin) {
363 ret = _plugin->write_func(data, size);
366 return ret;
369 std::shared_ptr<cygnal::Buffer>
370 Handler::readFromPlugin()
372 // GNASH_REPORT_FUNCTION;
374 std::shared_ptr<cygnal::Buffer> buf;
375 if (_plugin) {
376 buf = _plugin->read_func();
379 return buf;
382 bool
383 Handler::initialized()
385 // GNASH_REPORT_FUNCTION;
386 if (_files.empty()
387 && (_clients.size() == 1)
388 && !_local
389 && _remote.empty()
390 && !_plugin) {
391 return false;
394 return true;
397 // Find a stream in the vector or Disk Streams
398 std::shared_ptr<gnash::DiskStream>
399 Handler::findStream(const std::string &filespec)
401 // GNASH_REPORT_FUNCTION;
403 for (int i = 0; i < _streams; i++) {
404 if (_diskstreams[i]->getFilespec() == filespec) {
405 return _diskstreams[i];
409 return _diskstreams[0];
412 // Create a new DiskStream
413 double
414 Handler::createStream(double /* transid */)
416 GNASH_REPORT_FUNCTION;
418 _diskstreams[_streams]->setState(DiskStream::CREATED);
420 return _streams;
423 // Create a new DiskStream
424 double
425 Handler::createStream(double /* transid */, const std::string &filespec)
427 GNASH_REPORT_FUNCTION;
429 if (filespec.empty()) {
430 return -1;
433 _diskstreams[_streams]->setState(DiskStream::CREATED);
434 _diskstreams[_streams]->setFilespec(filespec);
436 return _streams;
439 bool
440 Handler::playStream()
442 GNASH_REPORT_FUNCTION;
444 // _diskstreams[int(streamid)]->setState(DiskStream::PLAY);
446 return false;
449 bool
450 Handler::playStream(const std::string &filespec)
452 GNASH_REPORT_FUNCTION;
454 std::shared_ptr<gnash::DiskStream> ds = _diskstreams[_streams];
456 string fullpath = crcfile.getDocumentRoot();
457 fullpath += "/";
458 fullpath += filespec;
459 log_debug("FILENAME: %s", fullpath);
461 // gnash::DiskStream &ds = findStream(filespec);
462 if (ds->getState() == DiskStream::CREATED) {
463 if (ds->open(fullpath)) {
464 ds->loadToMem(0); // FIXME: load only part of the whole file for now
465 ds->setState(DiskStream::PLAY);
466 return true;
470 return false;
473 // Publish a live stream
475 Handler::publishStream()
477 GNASH_REPORT_FUNCTION;
479 return publishStream("", Handler::LIVE);
483 Handler::publishStream(const std::string &/*filespec */, Handler::pub_stream_e /* op
486 GNASH_REPORT_FUNCTION;
488 // _diskstreams[int(streamid)]->setState(DiskStream::PUBLISH);
490 return -1;
493 // Seek within the RTMP stream
495 Handler::seekStream()
497 GNASH_REPORT_FUNCTION;
498 // _diskstreams[int(streamid)]->setState(DiskStream::SEEK);
500 return -1;
504 Handler::seekStream(int /* offset */)
506 GNASH_REPORT_FUNCTION;
507 // _diskstreams[int(streamid)]->setState(DiskStream::SEEK);
509 return -1;
512 // Pause the RTMP stream
514 Handler::pauseStream(double streamid)
516 GNASH_REPORT_FUNCTION;
518 _diskstreams[int(streamid)]->setState(DiskStream::PAUSE);
520 return -1;
523 // Pause the RTMP stream
525 Handler::togglePause(double streamid)
527 GNASH_REPORT_FUNCTION;
529 if (_diskstreams[int(streamid)]->getState() == DiskStream::PAUSE) {
530 _diskstreams[int(streamid)]->setState(DiskStream::PLAY);
531 } if (_diskstreams[int(streamid)]->getState() == DiskStream::PLAY) {
532 _diskstreams[int(streamid)]->setState(DiskStream::PAUSE);
535 return -1;
538 // Resume the paused RTMP stream
539 double
540 Handler::resumeStream(double streamid)
542 GNASH_REPORT_FUNCTION;
544 togglePause(streamid);
546 return -1;
549 // Close the RTMP stream
550 double
551 Handler::closeStream(double streamid)
553 GNASH_REPORT_FUNCTION;
555 _diskstreams[int(streamid)]->setState(DiskStream::CLOSED);
557 return -1;
560 // Delete the RTMP stream
561 double
562 Handler::deleteStream(double streamid)
564 GNASH_REPORT_FUNCTION;
566 _diskstreams[int(streamid)]->setState(DiskStream::NO_STATE);
568 _streams++;
570 return _streams;
573 // Dump internal data.
574 void
575 Handler::dump()
577 const char *proto_str[] = {
578 "NONE",
579 "HTTP",
580 "HTTPS",
581 "RTMP",
582 "RTMPT",
583 "RTMPTS",
584 "RTMPE",
585 "RTMPS",
586 "DTN"
589 // GNASH_REPORT_FUNCTION;
590 cerr << "Currently there are " <<_clients.size() << " clients connected."
591 << endl;
592 for (size_t i = 0; i < _clients.size(); i++) {
593 cerr << "Client on fd #" << _clients[i] << " is using "
594 << proto_str[_protocol[i]] << endl;
597 cerr << "Currently there are " << dec <<_diskstreams.size() << " DiskStreams."
598 << endl;
599 map<int, std::shared_ptr<DiskStream> >::iterator it;
600 for (it = _diskstreams.begin(); it != _diskstreams.end(); ++it) {
601 if (it->second) {
602 cerr << "DiskStream for fd #" << dec << it->first << endl;
603 it->second->dump();
609 } // end of gnash namespace
611 // local Variables:
612 // mode: C++
613 // indent-tabs-mode: t
614 // End: