2 * @file HTTPDaemon.cpp
\r
9 #include "Raym/Log.h"
\r
12 #include "ry0/iPTd/HTTPD.h"
\r
13 #include "ry0/iPTd/Controller.h"
\r
15 using namespace Raym;
\r
16 using namespace NET;
\r
39 HTTPD *HTTPD::alloc()
\r
44 HTTPD *HTTPD::initWithController(Controller *controller, int port, String *path)
\r
46 _controller = controller;
\r
47 _tuner_count = controller->_tuner_count;
\r
48 _tuners = controller->_tuners;
\r
51 _path = path->retain();
\r
60 _httpd = HTTPDaemon::alloc()->initWithPort(_port, 10);
\r
61 _httpd->setRootPath(_path);
\r
62 _httpd->setDelegate(this);
\r
64 return _httpd->start();
\r
72 HTTPResponse *HTTPD::request(HTTPRequest *request, SOCKADDR_IN *client)
\r
74 DebugLog2("%s\n", __FUNCTION__);
\r
78 EnterCriticalSection(&_cs);
\r
79 // flag = _initialized;
\r
80 LeaveCriticalSection(&_cs);
\r
86 HTTPResponse *response = NULL;
\r
88 if (request->method()->isEqualToString("GET") ||
\r
89 request->method()->isEqualToString("HEAD"))
\r
92 String *uri = request->URI();
\r
93 DebugLog0("request: %s\n", uri->cString());
\r
94 if (uri->isMatch("^/config.xml$"))
\r
96 RaymLock(_controller);
\r
97 response = responseWithDictionary(request, _controller->_props);
\r
98 RaymUnlock(_controller);
\r
100 else if (uri->isMatch("^/status.xml$"))
\r
102 RaymLock(_controller);
\r
103 response = responseWithDictionary(request, _controller->_status);
\r
104 RaymUnlock(_controller);
\r
109 else if (uri->isMatch("^/[0-9]{3}/"))
\r
111 // String::substringWithRange() の実装は後回しなので。。
\r
112 std::string s = uri->cString();
\r
113 int tuner = atoi(s.substr(1, 3).c_str());
\r
114 if ((0 <= tuner) && (tuner < _tuner_count))
\r
116 response = requestTunerControl(request, client, tuner);
\r
124 HTTPResponse *HTTPD::requestTunerControl(HTTPRequest *request, SOCKADDR_IN *client, int tuner)
\r
126 DebugLog0("%s\n", __FUNCTION__);
\r
128 HTTPResponse *result = NULL;
\r
131 RaymLock(_controller);
\r
134 String *uri = request->URI();
\r
135 while (uri != NULL)
\r
138 Dictionary *cgi = request->parseAsCGI();
\r
141 uri = cgi->stringForKey(HTTPRequest::KEY_CGI);
\r
148 if (uri->isMatch("^/iptv.m3u8$"))
\r
150 return responseForPlaylist(request, client);
\r
155 // /ttt/channel=nnn
\r
157 else if (uri->isMatch("^/[0-9]{3}/channel=[0-9]{1,3}$") && (cgi == NULL))
\r
159 String *ch = uri->substringFromIndex(13);
\r
164 int channel = ch->intValue();
\r
165 DebugLog2("set channel:%d(%s)\n", channel, ch->cString());
\r
166 if (_controller->setChannel(tuner, channel))
\r
169 DebugLog2("success.\n");
\r
170 result = responseForSuccess(request);
\r
175 DebugLog2("failed.\n");
\r
176 result = responseForFailed(request);
\r
182 // /ttt/recording=on?hour=hh&min=mm[&channel=nnn]
\r
184 else if (uri->isMatch("^/[0-9]{3}/recording=on$") && (cgi != NULL))
\r
187 Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);
\r
188 if (params == NULL)
\r
194 if ((params->count() != 2) && (params->count() != 3))
\r
200 String *p_hour = NULL;
\r
201 String *p_min = NULL;
\r
202 String *p_channel = NULL;
\r
211 {"hour", &p_hour, "^[0-2][0-9]$"},
\r
212 {"min", &p_min, "^[0-5][0-9]$"},
\r
213 {"channel", &p_channel, "^[0-9]{3}$"},
\r
217 for (uint i = 0; cgi[i].name != NULL; ++i)
\r
219 *(cgi[i].variable) = NULL;
\r
220 for (uint j = 0; j < params->count(); ++j)
\r
222 Dictionary *param = (Dictionary *)params->objectAtIndex(j);
\r
223 String *value = param->stringForKey(cgi[i].name);
\r
224 if ((value != NULL) && value->isMatch(cgi[i].regex))
\r
226 *(cgi[i].variable) = value;
\r
232 if ((p_hour == NULL) || (p_min == NULL))
\r
239 if (p_channel != NULL)
\r
241 channel = p_channel->intValue();
\r
245 channel = _tuners[tuner]->channel();
\r
251 int hour = p_hour->intValue();
\r
252 int min = p_min->intValue();
\r
256 Dictionary *epg = Dictionary::dictionaryWithCapacity(0);
\r
261 now += 1; // margin
\r
263 if (localtime_s(&tm, &now) != 0)
\r
270 end.tm_hour += hour;
\r
272 end.tm_sec += 1; // margin
\r
273 if (mktime(&end) == -1)
\r
282 sprintf_s(tmp, sizeof(tmp), "%04d/%02d/%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
\r
283 epg->setString(tmp, KEY_EPG_DATE);
\r
286 sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);
\r
287 epg->setString(tmp, KEY_EPG_START);
\r
290 sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", end.tm_hour, end.tm_min, end.tm_sec);
\r
291 epg->setString(tmp, KEY_EPG_END);
\r
294 sprintf_s(tmp, sizeof(tmp), "%d", channel);
\r
295 epg->setString(tmp, KEY_EPG_CHANNEL);
\r
298 epg->setString("off", KEY_EPG_REPEAT);
\r
301 epg->setString("ready", KEY_EPG_STATUS);
\r
309 if (_controller->_reservation->reserve(tuner, epg))
\r
311 result = responseForSuccess(request);
\r
315 result = responseForFailed(request);
\r
324 // /ttt/recording=off
\r
326 else if (uri->isMatch("^/[0-9]{3}/recording=off$") && (cgi == NULL))
\r
329 DebugLog2("recording off: %s\n", uri->cString());
\r
330 if (_controller->_reservation->cancel(tuner, -1))
\r
333 DebugLog2("success.\n");
\r
334 result = responseForSuccess(request);
\r
339 DebugLog2("failed.\n");
\r
340 result = responseForFailed(request);
\r
346 // /ttt/streaming=on?udp=nnnnn(&host=aaaaaa)
\r
348 else if (uri->isMatch("^/[0-9]{3}/streaming=on$") && (cgi != NULL))
\r
351 Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);
\r
352 if (params == NULL)
\r
358 if ((params->count() != 1) && (params->count() != 2))
\r
364 String *p_udp = NULL;
\r
365 String *p_host = NULL;
\r
374 {"udp", &p_udp, "^[0-9]{1,5}$"},
\r
375 {"host", &p_host, "^.+$"},
\r
379 for (uint i = 0; cgi[i].name != NULL; ++i)
\r
381 *(cgi[i].variable) = NULL;
\r
382 for (uint j = 0; j < params->count(); ++j)
\r
384 Dictionary *param = (Dictionary *)params->objectAtIndex(j);
\r
385 String *value = param->stringForKey(cgi[i].name);
\r
386 if ((value != NULL) && value->isMatch(cgi[i].regex))
\r
388 *(cgi[i].variable) = value;
\r
399 SOCKADDR_IN dst_addr;
\r
401 if (p_host != NULL)
\r
404 std::string host = udpstr.substr(idx + 5);
\r
405 udpstr = udpstr.substr(0, idx - 1);
\r
406 DebugLog2("udp: %s\n", udpstr.c_str());
\r
407 DebugLog2("host: %s\n", host.c_str());
\r
408 struct hostent *ent = gethostbyname(host.c_str());
\r
413 memcpy(&dst_addr, client, sizeof(SOCKADDR_IN));
\r
415 dst_addr.sin_port = htons(p_udp->intValue());
\r
417 if (_tuners[tuner]->startStreaming(&dst_addr))
\r
420 DebugLog2("success.\n");
\r
421 result = responseForSuccess(request);
\r
426 DebugLog2("failed.\n");
\r
427 result = responseForFailed(request);
\r
433 // /ttt/streaming=off(?host=aaaa)
\r
435 else if (uri->isMatch("^/[0-9]{3}/streaming=off$"))
\r
438 String *p_host = NULL;
\r
443 Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);
\r
444 if (params == NULL)
\r
450 if ((params->count() != 0) && (params->count() != 1))
\r
462 {"host", &p_host, "^.+$"},
\r
466 for (uint i = 0; cgi[i].name != NULL; ++i)
\r
468 *(cgi[i].variable) = NULL;
\r
469 for (uint j = 0; j < params->count(); ++j)
\r
471 Dictionary *param = (Dictionary *)params->objectAtIndex(j);
\r
472 String *value = param->stringForKey(cgi[i].name);
\r
473 if ((value != NULL) && value->isMatch(cgi[i].regex))
\r
475 *(cgi[i].variable) = value;
\r
481 SOCKADDR_IN dst_addr;
\r
482 if (p_host != NULL)
\r
489 _tuners[tuner]->stopStreaming();
\r
492 DebugLog2("success.\n");
\r
493 result = responseForSuccess(request);
\r
499 else if (uri->isMatch("^/[0-9]{3}/[0-9]{3}/streaming(-[^\\.]+)?.m3u8$") && (cgi == NULL))
\r
501 DebugLog0("uri: %s", uri->cString());
\r
505 int ch = uri->substringFromIndex(5)->substringToIndex(3)->intValue();
\r
506 DebugLog0("ch: %d", ch);
\r
508 // presetが指定されている場合、presetを抽出
\r
509 String *preset = NULL;
\r
510 if (uri->isMatch("streaming-"))
\r
512 preset = uri->substringFromIndex(19);
\r
513 preset = preset->substringToIndex(preset->length() - 5);
\r
514 DebugLog0("opt: preset: %s", preset->cString());
\r
519 preset = NSString::stringWithUTF8String(KEY_DEFAULT);
\r
522 // チャンネル/presetが有効か確認
\r
523 if (isChannelEnabled(tuner, ch) &&
\r
524 (_props->dictionaryForKey(KEY_PRESETS) != NULL) &&
\r
525 (_props->dictionaryForKey(KEY_PRESETS)->objectForKey(preset) != NULL))
\r
528 result = responseForHLSControl(request, client, tuner, ch, preset);
\r
532 result = responseForFailed(request);
\r
534 DebugLog0("hls req. done");
\r
537 else if (uri->isMatch("^/[0-9]{3}/[0-9]{3}/streaming-[0-9]+.ts$") && (cgi == NULL))
\r
540 DebugLog0("uri: %s", uri->cString());
\r
547 RaymUnlock(_controller);
\r
552 HTTPResponse *HTTPD::responseForPlaylist(HTTPRequest *request, SOCKADDR_IN *client)
\r
554 HTTPResponse *result = NULL;
\r
555 // http://bit.ly/iptv_feb2015
\r
561 #EXTINF:-1, [COLOR yellow]Updated 15/04/2015 @ 03:45 [/COLOR]
\r
562 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM
\r
563 #EXTINF:-1, [COLOR green] --Uk Live Tv--[/COLOR]
\r
564 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM
\r
565 #EXTINF:-1, [COLOR green] --Free Collection of IPTV sports links--[/COLOR]
\r
566 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM
\r
567 #EXTINF:-1, [COLOR red] --Links will go down. Please note that it will take time to update--[/COLOR]
\r
568 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM
\r
569 #EXTINF:-1, [COLOR red] --Please contact me at the husham.com website--[/COLOR]
\r
570 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM
\r
572 #EXTINF:-1, Sky sports news
\r
573 rtmp://89.248.172.159:443/liverepeater playpath=35 swfUrl=http://popeoftheplayers.eu/atdedead.swf pageUrl=http://popeoftheplayers.eu/crichd.php?id=35&width=600&height=450 token=#atd%#$ZH
\r
574 #EXTINF:-1,Bt sports 1
\r
575 rtmp://80.82.78.87:443/liverepeater/starsp pageUrl=http://xxxxxxxxxxxxxxxx.xx/rtmpe://strm.dcast.tv:1935/live/asdfadfaa/pageUrl=http://xxxxx.xx/rtmp://80.82.64.90:443/liverepeater/79/pageUrl=http://filotv.pw/rtmpe://strm.ukcast.tv:1935/redirect/FUNKTSN/pageUrl=http://ukcast.tv/rtmp://173.192.81.176:443/liverepeater/stream1/token%ZZri(nKa@#Z/pageUrl=http://hdcast.org/cdn.kingofplayers.com/rtmpe://46.246.29.152:1935/redirect/HDMNBC playpath=41?18?49?33?48?38?11 pageUrl=http://popeoftheplayers.eu/hdcast.org/rtmp://31.220.0.134:1935/live/tsn2/pageUrl=http://www.eucast.tv/rtmp://195.154.236.152:80/liverepeater/141449/pageUrl=http://goodcast.pw/rtmp://77.81.98.134/tv/bt1h28qn?v=pageUrl=http://castok.com/rtmp://89.46.102.70:443/liveedge/bt1pageUrl=http://hqstreams.tv/rtmpe://play.finecast.tv/live/hqbt1page/playpath=42?finecast.tv/rtmpe://cdn.hdcast.org:1935/redirect/swfUrl=http://www.hdcast.org/aplayer/jwplayer.flash.swfpageUrl=http://www.hdcast.org/token=Fo5_n0w?U.rA6l3-70w47ch@#8x12pX@ token=#atd%#$ZH
\r
580 std::string contents;
\r
581 contents = "#EXTM3U\r\n";
\r
582 contents += "\r\n";
\r
585 Dictionary *tuner_channel_to_udp = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_CHANNEL_TO_UDP);
\r
586 if (tuner_channel_to_udp != NULL)
\r
588 Tuner::Type types[] = {Tuner::ISDB_T, Tuner::ISDB_S};
\r
589 for (int t = 0; t < sizeof(types) / sizeof(Tuner::Type); ++t)
\r
591 for (int i = 0; i < _tunerCount; ++i)
\r
593 if (isTunerInitialized(i) && (_tuners[i]->type() == types[t]))
\r
595 uint ch_max = ((types[t] == Tuner::ISDB_T) ? Tuner::MAX_CHANNELS_ISDB_T : Tuner::MAX_CHANNELS_ISDB_S);
\r
596 for (uint ch = 0; ch <= ch_max; ++ch)
\r
598 if (isChannelEnabled(i, ch))
\r
600 char tuner_and_channel[10];
\r
601 sprintf_s(tuner_and_channel, "%d,%d", i, ch);
\r
602 char *chstr = strchr(tuner_and_channel, ',');
\r
608 NSString *udp_str = tuner_channel_to_udp->stringForKey(tuner_and_channel);
\r
609 if (udp_str != NULL)
\r
611 contents += "#EXTINF:-1 tvg-id=\"";
\r
612 contents += tuner_and_channel;
\r
613 contents += "\" tvg-logo=\"";
\r
614 contents += ((types[t] == Tuner::ISDB_T) ? "t_" : "s_");
\r
621 contents += "ffff";
\r
623 contents += "\" group-title=\"";
\r
624 contents += _tuners[i]->name();
\r
625 contents += "\", ";
\r
626 NSString *station_name = stationName(types[t], ch);
\r
627 if (station_name != NULL)
\r
629 contents += station_name->cString();
\r
633 contents += tuner_and_channel;
\r
635 contents += "\r\n";
\r
636 contents += "udp://0.0.0.0:";
\r
637 contents += udp_str->cString();
\r
638 contents += "\r\n";
\r
648 String *text = String::stringWithUTF8String(contents.c_str());
\r
651 result = responseWithUTF8Text(request, text);
\r
657 // positive response by XML
\r
658 HTTPResponse *HTTPD::responseForSuccess(HTTPRequest *request)
\r
660 Dictionary *dict = Dictionary::dictionaryWithCapacity(0);
\r
661 dict->setString("Success", KEY_RESULT);
\r
662 return responseWithDictionary(request, dict);
\r
665 // negative response by XML
\r
666 HTTPResponse *HTTPD::responseForFailed(HTTPRequest *request)
\r
668 Dictionary *dict = Dictionary::dictionaryWithCapacity(0);
\r
669 dict->setString("Failed", KEY_RESULT);
\r
670 return responseWithDictionary(request, dict);
\r
673 HTTPResponse *HTTPD::responseWithDictionary(HTTPRequest *request, Dictionary *dictionary)
\r
675 HTTPResponse *result = NULL;
\r
676 if ((request != NULL) && (dictionary != NULL))
\r
678 std::string xml = dictionary->toString();
\r
681 InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();
\r
684 // Content-Encoding
\r
687 header->setFieldBodyWithName("application/xml", "Content-Type");
\r
689 // Tranfer-Encoding
\r
691 header->setFieldBodyWithName(String::stringWithFormat("%I64u", xml.length()), "Content-Length");
\r
694 InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(String::stringWithUTF8String(xml.c_str()));
\r
697 InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);
\r
700 if (message != NULL)
\r
702 result = HTTPResponse::alloc()->init();
\r
703 result->autorelease();
\r
704 result->setVersion(request->version());
\r
705 result->setReason(NET::HTTPDaemon::reasonForStatus(200));
\r
706 result->setStatus(200);
\r
707 result->setMessage(message);
\r
714 HTTPResponse *HTTPD::responseWithUTF8Text(HTTPRequest *request, String *text)
\r
716 HTTPResponse *result = NULL;
\r
717 if ((text != NULL) && (request != NULL))
\r
720 InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();
\r
723 // Content-Encoding
\r
726 header->setFieldBodyWithName("text/plane; charset=UTF-8", "Content-Type");
\r
728 // Tranfer-Encoding
\r
730 header->setFieldBodyWithName(String::stringWithFormat("%I64u", text->length()), "Content-Length");
\r
733 InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(text);
\r
736 InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);
\r
739 if (message != NULL)
\r
741 // result = HTTPResponse::response();
\r
742 result = HTTPResponse::alloc()->init();
\r
743 result->setVersion(request->version());
\r
744 result->setReason(NET::HTTPDaemon::reasonForStatus(200));
\r
745 result->setStatus(200);
\r
746 result->setMessage(message);
\r
747 result->autorelease();
\r