OSDN Git Service

0.01版
[iptd/iPTd.git] / src / ry0 / iPTd / Controller.cpp
1 //\r
2 //\r
3 //\r
4 \r
5 //#include "stdafx.h"\r
6 \r
7 #define DBG_LEVEL 0\r
8 #include <Raym/Log.h>\r
9 \r
10 #include <time.h>\r
11 #include <direct.h>\r
12 #include <sys/types.h>\r
13 #include <sys/stat.h>\r
14 #include <stdio.h>\r
15 #include <fcntl.h>\r
16 #include <io.h>\r
17 #include <share.h>\r
18 #include <winsock2.h>\r
19 #include <ws2tcpip.h>\r
20 #include <Iphlpapi.h>\r
21 \r
22 #include "ry0/iPTd/Controller.h"\r
23 #include "ry0/iPTd/HTTPLiveStreaming.h"\r
24 #include "mpeg2/ts/Analyzer.h"\r
25 \r
26 #include "b25/arib_std_b25.h"\r
27 #include "b25/b_cas_card.h"\r
28 \r
29 using namespace Raym;\r
30 using namespace NET;\r
31 using namespace ry0::device;\r
32 \r
33 namespace ry0\r
34 {\r
35 namespace iPTd\r
36 {\r
37 \r
38 static const char *PLIST_PREFIX = "com.gmail.tim.and.pom";\r
39 \r
40 // プロパティデフォルト値\r
41 static const char * DEF_NAME                = "iPTd_R2";\r
42 static const char * DEF_HOSTNAME            = "localhost";\r
43 static const int    DEF_HTTP_PORT           = 50080;        // HTTPポート\r
44 static const int    DEF_BEGIN_UDP_PORT      = 51000;        // UDPポートの開始位置\r
45 static const int    DEF_SUSPEND_TIME        = 5;            // 休止するまでの時間(分単位)\r
46 static const int    DEF_FORCED_SUSPEND_TIME = 120;          // 強制休止するまでの時間(分単位)\r
47 static const char * DEF_COLLECT_EPG_TIME    = "02:00:00";   // EPG収集する時刻 HH:MM::SS\r
48 \r
49 // 非同期実行コマンド\r
50 static const long long CMD_RESTART              = 0x0001;  // 再開処理\r
51 static const long long CMD_SUSPEND              = 0x0002;  // サスペンド\r
52 static const long long CMD_PERIODIC             = 0x0003;  // 周期処理\r
53 static const long long CMD_PERIODIC_2           = 0x0004;  // 周期処理2\r
54 static const long long CMD_COLLECT_EPG_ISDB_S   = 0x0005;  // 番組情報取得(ISDB-S)\r
55 static const long long CMD_COLLECT_EPG_ISDB_T   = 0x0006;  // 番組情報取得(ISDB-T)\r
56 \r
57 //\r
58 static const TimeInterval DEF_COLLECT_EPG_DELAY    = 1.0;\r
59 static const TimeInterval DEF_COLLECT_EPG_RETRY    = 60.0;\r
60 static const TimeInterval DEF_COLLECT_EPG_LIMIT_S  = 10.5;\r
61 static const TimeInterval DEF_COLLECT_EPG_LIMIT_T  = 20.5;\r
62 \r
63 static const time_t OFFSET_OF_START_TIME        = -2;       // 録画開始時刻の補正(秒単位)\r
64 static const time_t OFFSET_OF_END_TIME          = -3;       // 録画停止時刻の補正(秒単位)\r
65 static const time_t OFFSET_OF_WAKEUP            = -240;     // 起動スケジュールの補正(秒単位)  注:休止するまでの時間(DEF_SUSPEND_TIME)よりも短くすること\r
66 static const time_t OFFSET_OF_SUPPRESSION_TIME  = -600;     // 録画開始前に休止の抑制を開始する時間(秒単位)\r
67 \r
68 \r
69 #if 0\r
70 \r
71 void Controller::delaySuspend()\r
72 {\r
73     // \r
74     Timer *timer = Timer::scheduledTimerWithTimeInterval(1.0, this, (void *)CMD_SUSPEND, false);\r
75     if (timer == NULL)\r
76     {\r
77         DebugLog0("Can't start  timer.");\r
78     }\r
79 }\r
80 #endif\r
81 \r
82 std::string Controller::createVideoPath(int tuner)\r
83 {\r
84     DebugLog2("Controller::createVideoPath()");\r
85 \r
86     std::string result = "";\r
87 \r
88     while (true)\r
89     {\r
90         time_t now;\r
91         time(&now);\r
92         TM tm;\r
93         if (localtime_s(&tm, &now) != 0)\r
94         {\r
95             break;\r
96         }\r
97 \r
98         result = _store_path->cString();\r
99         DebugLog2("result: %s\n", result.c_str());\r
100 \r
101         char tmp[128];\r
102         if (sprintf_s(tmp, sizeof(tmp), "\\%04d", tm.tm_year + 1900) < 0)\r
103         {\r
104             DebugLog0("sprintf_s() error: year\n");\r
105             result = "";\r
106             break;\r
107         }\r
108         result += tmp;\r
109         DebugLog2("result: %s\n", result.c_str());\r
110 \r
111         STAT stat;\r
112         if (_stat(result.c_str(), &stat) != 0)\r
113         {\r
114             if (_mkdir(result.c_str()) != 0)\r
115             {\r
116                 DebugLog0("_mkdir() error: year\n");\r
117                 result = "";\r
118                 break;\r
119             }\r
120             _stat(result.c_str(), &stat);\r
121         }\r
122         if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)\r
123         {\r
124             DebugLog0("%s is not directory.\n", result.c_str());\r
125             result = "";\r
126             break;\r
127         }\r
128 \r
129         if (sprintf_s(tmp, sizeof(tmp), "\\%02d", tm.tm_mon + 1) < 0)\r
130         {\r
131             DebugLog0("sprintf_s() error: month\n");\r
132             result = "";\r
133             break;\r
134         }\r
135         result += tmp;\r
136         DebugLog2("result: %s\n", result.c_str());\r
137 \r
138         if (_stat(result.c_str(), &stat) != 0)\r
139         {\r
140             if (_mkdir(result.c_str()) != 0)\r
141             {\r
142                 DebugLog0("_mkdir() error: month\n");\r
143                 result = "";\r
144                 break;\r
145             }\r
146             _stat(result.c_str(), &stat);\r
147         }\r
148         if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)\r
149         {\r
150             DebugLog0("%s is not directory.", result.c_str());\r
151             result = "";\r
152             break;\r
153         }\r
154 \r
155         if (sprintf_s(tmp, sizeof(tmp),\r
156                       "\\%04d%02d%02d_%02d%02d%02d_%03d_%s.ts",\r
157                       tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,\r
158                       _tuners[tuner]->channel(), _tuners[tuner]->name()) < 0)\r
159         {\r
160             DebugLog0("sprintf_s() error: filename");\r
161             result = "";\r
162             break;\r
163         }\r
164         result += tmp;\r
165         DebugLog2("result: %s\n", result.c_str());\r
166 \r
167         break;\r
168     }\r
169 \r
170     return result;\r
171 }\r
172 \r
173 #ifndef _WIN32\r
174 #pragma mark '\r
175 #pragma mark ------- EPG関連 -------\r
176 #endif\r
177 \r
178 void Controller::removePastEPGs()\r
179 {\r
180     DebugLog2("Controller::removePastEPGs()");\r
181 \r
182     // 現在時刻\r
183     time_t now = time(NULL);\r
184 \r
185     // lock\r
186     RaymLock(this);\r
187 \r
188     // サービスIDでループ\r
189     Array *keys1 = _epgs->allKeys();\r
190     for (uint i = 0; i < keys1->count(); ++i)\r
191     {\r
192         // イベント取得\r
193         Dictionary *events = _epgs->dictionaryForKey((String *)keys1->objectAtIndex(i));\r
194         if (events != NULL)\r
195         {\r
196             // イベントでループ\r
197             Array *keys2 = events->allKeys();\r
198             for (uint j = 0; j < keys2->count(); ++j)\r
199             {\r
200                 // キーとEPG取得\r
201                 String *key = (String *)keys2->objectAtIndex(j);\r
202                 Dictionary *epg = events->dictionaryForKey(key);\r
203                 if (epg != NULL)\r
204                 {\r
205                     // EPG取得できた場合\r
206                     time_t start = 0;\r
207                     time_t end = 0;\r
208                     getTimeWithEPG(epg, &start, &end);\r
209 \r
210                     // 終了時刻が過ぎていたら\r
211                     if (now > end)\r
212                     {\r
213                         // 削除する\r
214                         events->removeObjectForKey(key);\r
215                     }\r
216                 }\r
217             }\r
218         }\r
219     }\r
220 \r
221     // unlock\r
222     RaymUnlock(this);\r
223 }\r
224 \r
225 void Controller::collectEPGsForTuner(int tuner, TimeInterval limit)\r
226 {\r
227     DebugLog2("Controller::collectEPGsForTuner(%d) start.", tuner);\r
228 \r
229     // 既にロックされた状態でコールされる前提\r
230     bool locked = false;\r
231     RaymLock(this);\r
232     if ((0 <= tuner) && (tuner < _tunerCount))\r
233     {\r
234         locked = _tuners[tuner]->isLocked();\r
235     }\r
236     RaymUnlock(this);\r
237     if (!locked)\r
238     {\r
239         DebugLog2("Controller::collectEPGsForTuner(%d) end(no locked).", tuner);\r
240         return;\r
241     }\r
242 \r
243     // \r
244     Array *collected = Array::arrayWithCapacity(0);\r
245 \r
246     // 開始時刻取得\r
247     Date *start = Date::date();\r
248 \r
249     // 取得開始\r
250     bool done = false;\r
251     while (!done)\r
252     {\r
253         AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
254 \r
255         // EPG取得\r
256         MPEG2::TS::Analyzer an;\r
257         an.setFlag(MPEG2::TS::Analyzer::FLAG_EIT);\r
258         _tuners[tuner]->setListener(&an);\r
259         Array *epgs = an.epgInfos();\r
260         _tuners[tuner]->setListener(NULL);\r
261 \r
262         if (epgs != NULL)\r
263         {\r
264             // 取得成功\r
265             collected->addObjectsFromArray(epgs);\r
266         }\r
267 \r
268         // リミットチェック\r
269         if (Date::date()->timeIntervalSinceDate(start) >= limit)\r
270         {\r
271             done = true;\r
272         }\r
273 \r
274         // キャンセル確認\r
275         RaymLock(this);\r
276         if (!done)\r
277         {\r
278             done = _cancel_epg_collect;\r
279         }\r
280         RaymUnlock(this);\r
281 \r
282         pool->release();\r
283     }\r
284 \r
285     // lock\r
286     RaymLock(this);\r
287 \r
288     for (uint j = 0; j < collected->count(); ++j)\r
289     {\r
290         Dictionary *epg1 = (Dictionary *)collected->objectAtIndex(j);\r
291 \r
292         if (epg1->stringForKey(KEY_EPG_TITLE) == NULL)\r
293         {\r
294             // タイトルが無い場合は不要\r
295             continue;\r
296         }\r
297 \r
298         // Service ID を Primary Key\r
299         String *key = epg1->stringForKey(KEY_EPG_SERVICE_ID);\r
300         if (key != NULL)\r
301         {\r
302             Dictionary *epgs_of_service = _epgs->dictionaryForKey(key);\r
303             if (epgs_of_service == NULL)\r
304             {\r
305                 // 無い場合は作成\r
306                 epgs_of_service = Dictionary::dictionaryWithCapacity(0);\r
307                 _epgs->setObject(epgs_of_service, key);\r
308             }\r
309             // Event ID を Secondary Key\r
310             key = epg1->stringForKey(KEY_EPG_EVENT_ID);\r
311             if (key != NULL)\r
312             {\r
313                 epgs_of_service->setObject(epg1, key);\r
314             }\r
315         }\r
316     }\r
317 \r
318     // ファイルへ書き出し\r
319     _epgs->writeToFile(_epgs_path, true);\r
320 \r
321     // unlock\r
322     RaymUnlock(this);\r
323 \r
324     DebugLog2("Controller::collectEPGsForTuner(%d) end.", tuner);\r
325 }\r
326 \r
327 bool Controller::collectEPGs(Tuner::Type type)\r
328 {\r
329     DebugLog0("Controller::collectEPGs(%s) start.", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
330 \r
331     // 使用するチューナを決定\r
332     int tuner = 0;\r
333     bool locked = false;\r
334     RaymLock(this);\r
335     for (int i = _tunerCount - 1; i >= 0; --i)\r
336     {\r
337         if (isTunerEnabled(i))\r
338         {\r
339             if ((!_tuners[i]->isLocked()) && (_tuners[i]->type() == type))\r
340             {\r
341                 if (_tuners[i]->lock())\r
342                 {\r
343                     tuner = i;\r
344                     locked = true;\r
345                     break;\r
346                 }\r
347             }\r
348         }\r
349     }\r
350     RaymUnlock(this);\r
351 \r
352     // ロック確認\r
353     if (!locked)\r
354     {\r
355         DebugLog0("Controller::collectEPGs(%s) end(Can't locled).", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
356         // ロックできない場合は収集しない\r
357         return false;\r
358     }\r
359 \r
360     bool canceled = false;\r
361     DebugLog0("start collect EPG of \"%s(#%d)\".", _tuners[tuner]->name(), tuner);\r
362 \r
363     // 現在のチャンネルを保存\r
364     int channel = _tuners[tuner]->channel();\r
365 \r
366     // チャンネルを変更しつつEPGを取得\r
367     int max_channel = (type == Tuner::ISDB_S ? Tuner::MAX_CHANNELS_ISDB_S : Tuner::MAX_CHANNELS_ISDB_T);\r
368     for (int ch = 0; ch <= max_channel; ++ch)\r
369     {\r
370         // チャンネルが有効かチェック\r
371         if (isChannelEnabled(tuner, ch))\r
372         {\r
373             // チャンネル設定\r
374             if (_tuners[tuner]->setChannel(ch))\r
375             {\r
376                 // EPG取集\r
377                 collectEPGsForTuner(tuner, ((type == Tuner::ISDB_S) ? DEF_COLLECT_EPG_LIMIT_S : DEF_COLLECT_EPG_LIMIT_T));\r
378 \r
379                 // キャンセル確認\r
380                 RaymLock(this);\r
381                 if (_cancel_epg_collect)\r
382                 {\r
383                     ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
384                     canceled = true;\r
385                 }\r
386                 RaymUnlock(this);\r
387 \r
388                 // キャンセル確認\r
389                 //   終了不可 -> 録画待機中/録画中  なので、収集は諦める\r
390                 if (!canTerminate())\r
391                 {\r
392                     ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
393                     canceled = true;\r
394                 }\r
395             }\r
396         }\r
397     }\r
398 \r
399     // 元のチャンネルに戻す\r
400     _tuners[tuner]->setChannel(channel);\r
401 \r
402     // lock\r
403     RaymLock(this);\r
404 \r
405     // チューナをアンロック\r
406     _tuners[tuner]->unlock();\r
407 \r
408     // unlock\r
409     RaymUnlock(this);\r
410 \r
411     if (canceled)\r
412     {\r
413         DebugLog0("collect EPG of \"%s\" was canceled.", _tuners[tuner]->name());\r
414     }\r
415     else\r
416     {\r
417         DebugLog0("collect EPG of \"%s\" was finished.", _tuners[tuner]->name());\r
418 \r
419         // 過去のEPGを削除\r
420         removePastEPGs();\r
421     }\r
422 \r
423     DebugLog2("Controller::collectEPGs(%s) end.", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
424 \r
425     return true;\r
426 }\r
427 \r
428 #ifndef _WIN32\r
429 #pragma mark '\r
430 #pragma mark ------- 予約録画関連 -------\r
431 #endif\r
432 \r
433 //\r
434 // 録画予約:サービスID/イベントID指定\r
435 //   EPGデータから指定のサービスID/イベントIDのEPGを取り出し、EPG指定の録画予約をコール\r
436 //\r
437 bool Controller::reserve(int service_id, int event_id)\r
438 {\r
439     DebugLog2("Controller::reserve(service_id, event_id)");\r
440 \r
441     bool result = false;\r
442 \r
443     // lock\r
444     RaymLock(this);\r
445 \r
446     if (_epgs != NULL)\r
447     {\r
448         Dictionary *events = _epgs->dictionaryForKey(String::stringWithFormat("%d", service_id));\r
449         if (events != NULL)\r
450         {\r
451             Dictionary *epg = events->dictionaryForKey(String::stringWithFormat("%d", event_id));\r
452             if (epg != NULL)\r
453             {\r
454                 result = reserve(epg);\r
455             }\r
456         }\r
457     }\r
458 \r
459     // unlock\r
460     RaymUnlock(this);\r
461 \r
462     return result;\r
463 }\r
464 \r
465 //\r
466 // 録画予約:EPG指定\r
467 //   EPGからサービスIDを取り出し、チューナ情報を検索して一致するサービスIDがあったらチューナ/EPG指定の録画予約を試行\r
468 //\r
469 bool Controller::reserve(Dictionary *in_epg)\r
470 {\r
471     DebugLog2("Controller::reserve(epg)");\r
472 \r
473     bool result = false;\r
474 \r
475     // lock\r
476     RaymLock(this);\r
477 \r
478     while (in_epg != NULL)\r
479     {\r
480         // EPGを複製\r
481         Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);\r
482         if (epg == NULL)\r
483         {\r
484             DebugLog3("Dictionary::dictionaryWithDictionary() ng.");\r
485             break;\r
486         }\r
487 \r
488         // サービスID取得\r
489         String *service_id = epg->stringForKey(KEY_EPG_SERVICE_ID);\r
490         if (service_id == NULL)\r
491         {\r
492             DebugLog3("epg->stringForKey(KEY_EPG_SERVICE_ID) ng.");\r
493             break;\r
494         }\r
495         DebugLog3("service_id: %s\n", service_id->cString());\r
496 \r
497         // 全チューナ情報取得\r
498         Dictionary *tunerInfos = _props->dictionaryForKey(KEY_TUNERS);\r
499         if (tunerInfos == NULL)\r
500         {\r
501             DebugLog3("_props->dictionaryForKey(KEY_TUNERS) ng.");\r
502             break;\r
503         }\r
504 \r
505         // チューナを検索\r
506         for (int i = 0; (!result) && (i < _tunerCount); ++i)\r
507         {\r
508             // チューナ情報取得\r
509             Dictionary *tunerInfo = tunerInfos->dictionaryForKey(_tuners[i]->name());\r
510             if (tunerInfo == NULL)\r
511             {\r
512                 DebugLog3("tunerInfos->dictionaryForKey(_tuners[%d]->name()) ng.", i);\r
513                 continue;\r
514             }\r
515 \r
516             // 全チャンネル情報取得\r
517             Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
518             if (channels == NULL)\r
519             {\r
520                 DebugLog3("tunerInfo->dictionaryForKey(KEY_CHANNELS) ng.");\r
521                 continue;\r
522             }\r
523 \r
524             // キー取得\r
525             Array *chkeys = channels->allKeys();\r
526             for (uint ch = 0; ch < chkeys->count(); ++ch)\r
527             {\r
528                 // チャンネル情報取得\r
529                 Dictionary *channel = channels->dictionaryForKey((String *)chkeys->objectAtIndex(ch));\r
530                 if (channel == NULL)\r
531                 {\r
532                     DebugLog3("channels->dictionaryForKey() ng.");\r
533                     continue;\r
534                 }\r
535 \r
536                 // 全サービス情報取得\r
537                 Array *services = (Array *)channel->objectForKey(KEY_SERVICES);\r
538                 if (services == NULL)\r
539                 {\r
540                     DebugLog3("channel->objectForKey() ng.");\r
541                     continue;\r
542                 }\r
543 \r
544                 for (uint s = 0; s < services->count(); ++s)\r
545                 {\r
546                     // サービス情報取得\r
547                     Dictionary *service = (Dictionary *)services->objectAtIndex(s);\r
548                     if (service == NULL)\r
549                     {\r
550                         DebugLog3("service->objectAtIndex() ng.");\r
551                         continue;\r
552                     }\r
553 \r
554                     // サービスIDを比較\r
555                     String *sid = service->stringForKey(KEY_SERVICE_ID);\r
556                     if ((sid != NULL) && sid->isEqualToString(service_id))\r
557                     {\r
558                         // チャンネルを設定\r
559                         epg->setString((String *)chkeys->objectAtIndex(ch), KEY_EPG_CHANNEL);\r
560 \r
561                         // 録画予約\r
562                         result = reserve(i, epg);\r
563 \r
564                         // チャンネルループのカウンタを更新(=ループを終了させる)\r
565                         ch = chkeys->count();\r
566                         break;\r
567                     }\r
568                 }\r
569             }\r
570         }\r
571 \r
572         break;\r
573     }\r
574 \r
575     // unlock\r
576     RaymUnlock(this);\r
577 \r
578     return result;\r
579 }\r
580 \r
581 //\r
582 // 録画予約:チューナ/EPG指定\r
583 //\r
584 bool Controller::reserve(int tuner, Dictionary *in_epg)\r
585 {\r
586     DebugLog2("Controller::reserve(tuner, epg)");\r
587 \r
588     bool result = false;\r
589 \r
590     // lock\r
591     RaymLock(this);\r
592 \r
593     while ((0 <= tuner) && (tuner < _tunerCount) && (in_epg != NULL))\r
594     {\r
595         Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);\r
596         if (epg == NULL)\r
597         {\r
598             DebugLog3("Dictionary::dictionaryWithDictionary() ng.");\r
599             break;\r
600         }\r
601 \r
602         Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
603         if (array == NULL)\r
604         {\r
605             array = Array::arrayWithCapacity(0);\r
606             _reservations->setObject(array, _tuners[tuner]->name());\r
607         }\r
608 \r
609         time_t epg_start;\r
610         time_t epg_end;\r
611         getTimeWithEPG(epg, &epg_start, &epg_end);\r
612         DebugLog2("epg start: %ld, end: %ld\n", epg_start, epg_end);\r
613 \r
614         time_t pre_start = 0;\r
615         time_t pre_end = 0;\r
616         pre_end = time(NULL);\r
617         DebugLog2("pre_end: %ld", pre_end);\r
618 \r
619         for (uint i = 0; i < array->count(); ++i)\r
620         {\r
621             Dictionary *cur = (Dictionary *)array->objectAtIndex(i);\r
622             time_t cur_start;\r
623             time_t cur_end;\r
624             getTimeWithEPG(cur, &cur_start, &cur_end);\r
625             DebugLog2("cur start: %ld, end: %ld\n", cur_start, cur_end);\r
626             if ((pre_end <= epg_start) && (epg_end <= cur_start))\r
627             {\r
628                 DebugLog2("insert: %d\n", i);\r
629                 array->insertObject(epg, i);\r
630                 result = true;\r
631                 break;\r
632             }\r
633             pre_start = cur_start;\r
634             pre_end = cur_end;\r
635         }\r
636 \r
637         if (!result)\r
638         {\r
639             if (pre_end <= epg_start)\r
640             {\r
641                 DebugLog2("add\n");\r
642                 array->addObject(epg);\r
643                 result = true;\r
644             }\r
645             else\r
646             {\r
647                 DebugLog2("no add\n");\r
648             }\r
649         }\r
650         if (result)\r
651         {\r
652             epg->setInteger(_reservation_seq_id, KEY_EPG_RESV_ID);\r
653             _reservation_seq_id = (_reservation_seq_id + 1) % 1000000;\r
654             _reservations->setInteger(_reservation_seq_id, KEY_EPG_LAST_RESV_ID);\r
655 \r
656             //\r
657             _reservations->writeToFile(_reservations_path, true);\r
658         }\r
659 \r
660         break;\r
661     }\r
662 \r
663     // unlock\r
664     RaymUnlock(this);\r
665 \r
666     return result;\r
667 }\r
668 \r
669 //\r
670 // tuner: 0 - (_tunerCount - 1)  cancel current\r
671 // tuner: -1  cancel reserve_id\r
672 //\r
673 bool Controller::cancel(int tuner, int reserve_id)\r
674 {\r
675     bool result = false;\r
676 \r
677     // lock\r
678     RaymLock(this);\r
679 \r
680     //\r
681     if ((0 <= tuner) && (tuner < _tunerCount))\r
682     {\r
683         Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
684         if (array != NULL)\r
685         {\r
686             if (array->count() > 0)\r
687             {\r
688                 Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
689                 String *status = epg->stringForKey(KEY_EPG_STATUS);\r
690                 if (status != NULL)\r
691                 {\r
692                     if (status->isEqualToString("running"))\r
693                     {\r
694                         epg->setString("stop", KEY_EPG_STATUS);\r
695                         result = true;\r
696                     }\r
697                 }\r
698             }\r
699         }\r
700     }\r
701 \r
702     //\r
703     else if ((tuner < 0) && (0 <= reserve_id) && (reserve_id < 1000000))\r
704     {\r
705         for (int i = 0; i < _tunerCount; ++i)\r
706         {\r
707             Array *array = _reservations->arrayForKey(_tuners[i]->name());\r
708             if (array != NULL)\r
709             {\r
710                 for (uint j = 0; j < array->count(); ++j)\r
711                 {\r
712                     Dictionary *epg = (Dictionary *)array->objectAtIndex(j);\r
713                     if (reserve_id == epg->integerForKey(KEY_EPG_RESV_ID))\r
714                     {\r
715                         String *status = epg->stringForKey(KEY_EPG_STATUS);\r
716                         if ((status != NULL) && status->isEqualToString("running"))\r
717                         {\r
718                             epg->setString("stop", KEY_EPG_STATUS);\r
719                         }\r
720                         else\r
721                         {\r
722                             array->removeObjectAtIndex(j);\r
723                         }\r
724                         result = true;\r
725                         break;\r
726                     }\r
727                 }\r
728             }\r
729             if (result)\r
730             {\r
731                 break;\r
732             }\r
733         }\r
734     }\r
735 \r
736     if (result)\r
737     {\r
738         _reservations->writeToFile(_reservations_path, true);\r
739     }\r
740 \r
741     // unlock\r
742     RaymUnlock(this);\r
743 \r
744     return result;\r
745 }\r
746 \r
747 //\r
748 // 録画制御\r
749 //\r
750 void Controller::periodic(void)\r
751 {\r
752     bool need_update = false;\r
753 \r
754 #ifdef OBJC_MEMORY_CHECK\r
755     DebugLog0("global_objc_count_ = %d", Raym::global_objc_count_);\r
756 #endif\r
757 \r
758     // lock\r
759     RaymLock(this);\r
760 \r
761     // 現在時刻取得\r
762     time_t now = time(NULL);\r
763 \r
764     DebugLog2("periodic: %d", now);\r
765 \r
766     //\r
767     for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
768     {\r
769         Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
770         if ((array == NULL) || (array->count() == 0))\r
771         {\r
772             // next tuner\r
773             continue;\r
774         }\r
775 \r
776         //\r
777         Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
778         time_t start;\r
779         time_t end;\r
780         getTimeWithEPG(epg, &start, &end);\r
781         \r
782         //\r
783         // 録画停止要否チェック\r
784         //\r
785         bool stop_need = false;\r
786         while (true)\r
787         {\r
788             String *status = epg->stringForKey(KEY_EPG_STATUS);\r
789             if (status != NULL)\r
790             {\r
791                 if (status->isEqualToString("stop"))\r
792                 {\r
793                     stop_need = true;\r
794                     break;\r
795                 }\r
796                 if (!status->isEqualToString("running"))\r
797                 {\r
798                     break;\r
799                 }\r
800             }\r
801             if (end + OFFSET_OF_END_TIME <= now)\r
802             {\r
803                 stop_need = true;\r
804             }\r
805             break;\r
806         }\r
807         if (stop_need)\r
808         {\r
809             DebugLog2("I try stop\n");\r
810             int fd =_tuners[tuner]->stopRecording();\r
811             if (fd < 0)\r
812             {\r
813                 DebugLog1("stopRecording() error.\n");\r
814             }\r
815             else\r
816             {\r
817                 DebugLog2("stopRecording() ok\n");\r
818                 DebugLog0("stop recording of \"%s\"", _tuners[tuner]->name());\r
819                 _close(fd);\r
820             }\r
821             array->removeObject(epg);\r
822             \r
823             if (array->count() > 0)\r
824             {\r
825                 epg = (Dictionary *)array->objectAtIndex(0);\r
826             }\r
827             else\r
828             {\r
829                 epg = NULL;\r
830             }\r
831             need_update = true;\r
832         }\r
833 \r
834         if (epg == NULL)\r
835         {\r
836             // next tuner\r
837             continue;\r
838         }\r
839 \r
840         //\r
841         // 録画開始要否チェック\r
842         //\r
843         bool start_need = false;\r
844         start = end = 0;\r
845         getTimeWithEPG(epg, &start, &end);\r
846         if ((start != 0) && (end != 0))\r
847         {\r
848             String *status = epg->stringForKey(KEY_EPG_STATUS);\r
849             if ((status == NULL) || !(status->isEqualToString("running")))\r
850             {\r
851                 if (end + OFFSET_OF_END_TIME <= now)\r
852                 {\r
853                     // 既に終了時間が経過しているので削除する\r
854                     array->removeObject(epg);\r
855                 }\r
856                 else if (start + OFFSET_OF_START_TIME <= now)\r
857                 {\r
858                     start_need = true;\r
859                 }\r
860             }\r
861         }\r
862 \r
863         if (start_need)\r
864         {\r
865             DebugLog2("I need start.\n");\r
866             String *ch = epg->stringForKey(KEY_EPG_CHANNEL);\r
867             if (ch != NULL)\r
868             {\r
869                 int channel = atoi(ch->cString());\r
870                 DebugLog2("channel: %d\n", channel);\r
871                 std::string videopath = createVideoPath(tuner);\r
872                 if (videopath != "")\r
873                 {\r
874                     DebugLog2("videopath: %s\n", videopath.c_str());\r
875                     int fd = -1;\r
876                     if (_sopen_s(&fd, videopath.c_str(),\r
877                                  (_O_CREAT | _O_EXCL | _O_WRONLY | _O_BINARY | _O_TRUNC), _SH_DENYRW, (_S_IREAD | _S_IWRITE)) == 0)\r
878                     {\r
879                         DebugLog2("open ok.\n");\r
880                         bool startResult = true;\r
881                         if (_tuners[tuner]->channel() != channel)\r
882                         {\r
883                             if (!setChannel(tuner, channel))\r
884                             {\r
885                                 DebugLog3("setChannel() ng.");\r
886                                 startResult = false;\r
887                             }\r
888                         }\r
889 \r
890                         if (startResult)\r
891                         {\r
892                             if (_tuners[tuner]->startRecording(fd))\r
893                             {\r
894                                 DebugLog2("startRecording() ok.");\r
895                                 DebugLog0("start recording of \"%s\" to %s.", _tuners[tuner]->name(), videopath.c_str());\r
896                             }\r
897                             else\r
898                             {\r
899                                 DebugLog3("Tuner::startRecording() failed.");\r
900                                 startResult = false;\r
901                             }\r
902                         }\r
903 \r
904                         if (startResult)\r
905                         {\r
906                             epg->setString("running", KEY_EPG_STATUS);\r
907                         }\r
908                         else\r
909                         {\r
910                             _close(fd);\r
911                         }\r
912                     }\r
913                     else\r
914                     {\r
915                         DebugLog0("open ng. 0x%08x\n", errno);\r
916                     }\r
917                 }\r
918                 else\r
919                 {\r
920                     DebugLog0("Can't create videopath.\n");\r
921                 }\r
922             }\r
923             else\r
924             {\r
925                 DebugLog0("error.\n");\r
926             }\r
927         }\r
928     }\r
929 \r
930     if (need_update)\r
931     {\r
932         //\r
933         _reservations->writeToFile(_reservations_path, true);\r
934     }\r
935 \r
936 #if 0\r
937     // EPG収集時刻を取得\r
938     String *collect_str = _props->stringForKey(KEY_COLLECT_EPG_TIME);\r
939     if (collect_str != NULL)\r
940     {\r
941         // 秒に変換\r
942         time_t collect_time = 0;\r
943         getTimeWithString(collect_str, &collect_time);\r
944 \r
945         // 現在時刻と比較\r
946         if ((collect_time <= now) && (now < collect_time + 1))\r
947         {\r
948             // タイマが起動中か確認\r
949             if ((_timer_epg_s == NULL) || !_timer_epg_s->valid())\r
950             {\r
951                 // EPG収集用タイマ起動(ISDB-S)\r
952                 RELEASE(_timer_epg_s);\r
953                 _timer_epg_s = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);\r
954                 if (_timer_epg_s != NULL)\r
955                 {\r
956                     _timer_epg_s->fire();\r
957                 }\r
958             }\r
959 \r
960             // タイマが起動中か確認\r
961             if ((_timer_epg_t == NULL) || !_timer_epg_t->valid())\r
962             {\r
963                 // EPG収集用タイマ起動(ISDB-T)\r
964                 RELEASE(_timer_epg_t);\r
965                 _timer_epg_t = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);\r
966                 if (_timer_epg_t != NULL)\r
967                 {\r
968                     _timer_epg_t->fire();\r
969                 }\r
970             }\r
971         }\r
972     }\r
973 #endif\r
974 \r
975     // unlock\r
976     RaymUnlock(this);\r
977 \r
978     //\r
979     // 1/100秒単位が 0 に近くなるように次回T.O.を微調整\r
980     // ただし、windowsは精度が低いので期待しないことw\r
981     //\r
982 \r
983 #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)\r
984     static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64;\r
985 #else\r
986     static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL;\r
987 #endif\r
988     // 現在時刻を取得\r
989     FILETIME ft;\r
990     GetSystemTimeAsFileTime(&ft);\r
991 \r
992     // EPOCH秒への変換\r
993     __time64_t now_sec;\r
994     __time64_t now_usec;\r
995     now_sec = ft.dwHighDateTime;\r
996     now_sec <<= 32;\r
997     now_sec |= ft.dwLowDateTime;\r
998     now_sec /= 10;  /*convert into microseconds*/\r
999     now_sec -= DELTA_EPOCH_IN_MICROSECS;\r
1000     now_usec = (now_sec % 1000000UL);\r
1001     now_sec = now_sec / 1000000UL;\r
1002 \r
1003     TimeInterval interval = (TimeInterval)now_usec;\r
1004     interval = interval / 1000000;\r
1005     _timer_periodic->setTimeInterval(1.005 - interval);\r
1006 \r
1007 #ifdef OBJC_MEMORY_CHECK\r
1008     DebugLog0("global_objc_count_ = %d", Raym::global_objc_count_);\r
1009 #endif\r
1010 }\r
1011 \r
1012 void Controller::updateKeywordsReservation()\r
1013 {\r
1014     DebugLog2("Controller::updateKeywordsReservation()");\r
1015 \r
1016     // lock\r
1017     RaymLock(this);\r
1018 \r
1019     // 予約情報からキーワード予約フラグが設定されているものを削除\r
1020     for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
1021     {\r
1022         Array *epgs = _reservations->arrayForKey(_tuners[tuner]->name());\r
1023         if (epgs != NULL)\r
1024         {\r
1025             for (uint epgs_idx_offset = epgs->count(); epgs_idx_offset > 0; --epgs_idx_offset)\r
1026             {\r
1027                 Dictionary *epg = (Dictionary *)epgs->objectAtIndex(epgs_idx_offset - 1);\r
1028                 if (epg->boolForKey(KEY_EPG_RESERVED_BY_KEYWORDS))\r
1029                 {\r
1030                     epgs->removeObject(epg);\r
1031                 }\r
1032             }\r
1033         }\r
1034     }\r
1035 \r
1036     // キーワードで検索\r
1037     Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
1038     if (keywords_info != NULL)\r
1039     {\r
1040         // キーワード有り\r
1041 \r
1042         // _epgs からキー(サービスID)を取得\r
1043         Array *service_id_keys = _epgs->allKeys();\r
1044 \r
1045         // サービスIDでループ\r
1046         for (uint service_id_keys_idx = 0; service_id_keys_idx < service_id_keys->count(); ++service_id_keys_idx)\r
1047         {\r
1048             // サービスID\r
1049             String *service_id = (String *)service_id_keys->objectAtIndex(service_id_keys_idx);\r
1050 \r
1051             // 局名\r
1052             String *stationName = stationNameForServiceID(service_id);\r
1053             if (stationName == NULL)\r
1054             {\r
1055                 // 局名が取得できなかったら次のサービスIDへ\r
1056                 continue;\r
1057             }\r
1058 \r
1059             // 指定サービスIDのEPGを取得\r
1060             Dictionary *events = _epgs->dictionaryForKey((String *)service_id_keys->objectAtIndex(service_id_keys_idx));\r
1061 \r
1062             // キー(イベントID)を取得\r
1063             Array *event_id_keys = events->allKeys();\r
1064 \r
1065             // イベントIDでループ\r
1066             for (uint event_id_keys_idx = 0; event_id_keys_idx < event_id_keys->count(); ++event_id_keys_idx)\r
1067             {\r
1068                 // 指定イベントIDのEPGを取得\r
1069                 Dictionary *epg = events->dictionaryForKey((String *)event_id_keys->objectAtIndex(event_id_keys_idx));\r
1070 \r
1071                 // 取得したEPGがキーワードの条件にマッチするか\r
1072 \r
1073                 time_t start;\r
1074                 getTimeWithString(epg->stringForKey(KEY_EPG_START), &start);\r
1075 \r
1076                 // キーワードの配列を取得\r
1077                 Array *keywords = keywords_info->allKeys();\r
1078                 for (uint keywords_idx = 0; keywords_idx < keywords->count(); ++keywords_idx)\r
1079                 {\r
1080                     // キーワード\r
1081                     String *kwd = (String *)keywords->objectAtIndex(keywords_idx);\r
1082 \r
1083                     // キーワード(分割)\r
1084                     Array *kwds = kwd->componentsSeparatedByString(",");\r
1085                     if (kwds == NULL)\r
1086                     {\r
1087                         continue;\r
1088                     }\r
1089 \r
1090                     // キーワード情報\r
1091                     Dictionary *kwdinf = keywords_info->dictionaryForKey(kwd);\r
1092 \r
1093                     // 局名フィルタ\r
1094                     String *kwdinf_service_id = kwdinf->stringForKey(KEY_EPG_SERVICE_ID);\r
1095                     if (kwdinf_service_id != NULL)\r
1096                     {\r
1097                         // 局名フィルタ有り\r
1098                         String *sname = stationNameForServiceID(kwdinf_service_id);\r
1099                         if (sname != NULL)\r
1100                         {\r
1101                             if (!stationName->isEqualToString(sname))\r
1102                             {\r
1103                                 // 局名が異なる場合は次のキーワードへ\r
1104                                 continue;\r
1105                             }\r
1106                         }\r
1107                     }\r
1108 \r
1109                     // 開始時刻フィルタ\r
1110                     String *kwdinf_start = kwdinf->stringForKey(KEY_EPG_START);\r
1111                     if (kwdinf_start != NULL)\r
1112                     {\r
1113                         // 開始時刻フィルタ有り\r
1114                         String *st = kwdinf_start->stringByAppendingString(":00");\r
1115                         time_t kwd_st, kwd_ed;\r
1116                         getTimeWithString(st, &kwd_st);\r
1117                         kwd_ed = kwd_st + 3600;\r
1118 \r
1119                         // 開始時刻フィルタの時刻 <= EPGの開始時刻 <= 開始時刻フィルタの時刻+60min ならOK\r
1120                         if ((kwd_st > start) || (start > kwd_ed))\r
1121                         {\r
1122                             // 範囲外なので次のキーワードへ\r
1123                             continue;\r
1124                         }\r
1125                     }\r
1126 \r
1127                     // タイトル\r
1128                     String *title = epg->stringForKey(KEY_EPG_TITLE);\r
1129                     bool title_flag = (title != NULL);\r
1130 \r
1131                     // 概要\r
1132                     String *desc = epg->stringForKey(KEY_EPG_DESCRIPTION);\r
1133                     bool desc_flag = (desc != NULL);\r
1134 \r
1135                     // キーワード(分割)がタイトル/概要のどちらかに、全て(AND)含まれていたら予約対象とする\r
1136                     for (uint kwds_idx = 0; kwds_idx < kwds->count(); ++kwds_idx)\r
1137                     {\r
1138                         String *kw = ((String *)kwds->objectAtIndex(kwds_idx))->stringByTrimming();\r
1139 \r
1140                         // タイトル\r
1141                         if (title != NULL)\r
1142                         {\r
1143                             Range r = title->rangeOfString(kw);\r
1144                             if (r.location == NotFound)\r
1145                             {\r
1146                                 title_flag = false;\r
1147                             }\r
1148                         }\r
1149 \r
1150                         // 概要\r
1151                         if (desc != NULL)\r
1152                         {\r
1153                             Range r = desc->rangeOfString(kw);\r
1154                             if (r.location == NotFound)\r
1155                             {\r
1156                                 desc_flag = false;\r
1157                             }\r
1158                         }\r
1159                     }\r
1160 \r
1161                     if (title_flag || desc_flag)\r
1162                     {\r
1163                         // タイトル/概要のどちらかに、キーワード(分割)が全て含まれていたので\r
1164                         // この EPG を予約対象とする\r
1165                         Dictionary *epg2 = Dictionary::dictionaryWithDictionary(epg);\r
1166                         if (epg2 != NULL)\r
1167                         {\r
1168                             DebugLog0("kwd: %s", kwd->cString());\r
1169                             if (title != NULL)\r
1170                             {\r
1171                                 DebugLog0("title: %s", title->cString());\r
1172                             }\r
1173                             if (desc != NULL)\r
1174                             {\r
1175                                 DebugLog0("desc: %s", desc->cString());\r
1176                             }\r
1177                             epg2->setBool(true, KEY_EPG_RESERVED_BY_KEYWORDS);\r
1178                             reserve(epg2);\r
1179                         }\r
1180                     }\r
1181                 }\r
1182             }\r
1183         }\r
1184     }\r
1185 \r
1186     // unlock\r
1187     RaymUnlock(this);\r
1188 }\r
1189 \r
1190 void Controller::updateSchedule()\r
1191 {\r
1192     DebugLog2("Controller::updateSchedule()");\r
1193 \r
1194     // lock\r
1195     RaymLock(this);\r
1196 \r
1197     // レジューム時刻\r
1198     time_t resume_time = 0;\r
1199 \r
1200     // EPG収集時刻を取得\r
1201     String *collect_epg_time = _props->stringForKey(KEY_COLLECT_EPG_TIME);\r
1202     if (collect_epg_time != NULL)\r
1203     {\r
1204         getTimeWithString(collect_epg_time, &resume_time);\r
1205     }\r
1206 \r
1207     // 録画予約をチェック\r
1208     for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
1209     {\r
1210         Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
1211         if ((array == NULL) || (array->count() == 0))\r
1212         {\r
1213             // next tuner\r
1214             continue;\r
1215         }\r
1216 \r
1217         Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
1218         time_t start = 0;\r
1219         time_t end = 0;\r
1220         getTimeWithEPG(epg, &start, &end);\r
1221         if ((start != 0) && (end != 0))\r
1222         {\r
1223             if (start < resume_time)\r
1224             {\r
1225                 resume_time = start;\r
1226             }\r
1227         }\r
1228     }\r
1229 \r
1230     //\r
1231     TM resume_tm;\r
1232     resume_time += OFFSET_OF_WAKEUP;    // 起動時刻を 5分前に設定(OFFSET_OF_WAKEUPで調整)\r
1233     if (localtime_s(&resume_tm, &resume_time) == 0)\r
1234     {\r
1235         resetWakeSchedule();\r
1236         if (setWakeSchedule(resume_tm.tm_year + 1900, resume_tm.tm_mon + 1, resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min))\r
1237         {\r
1238             DebugLog0("set wake schedule to \"%04d/%02d/%02d %02d:%02d:00\".",\r
1239                       resume_tm.tm_year + 1900, resume_tm.tm_mon + 1, resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min);\r
1240         }\r
1241     }\r
1242 \r
1243     // unlock\r
1244     RaymUnlock(this);\r
1245 }\r
1246 \r
1247 #ifndef _WIN32\r
1248 #pragma mark '\r
1249 #pragma mark ------- チューナ制御 -------\r
1250 #endif\r
1251 \r
1252 void Controller::scanChannel(int tuner)\r
1253 {\r
1254     DebugLog2("Controller::scanChannel(%d)", tuner);\r
1255 \r
1256     if ((tuner < 0) || (_tunerCount <= tuner))\r
1257     {\r
1258         DebugLog3("Invalid tuner: %d", tuner);\r
1259         return;\r
1260     }\r
1261 \r
1262     DebugLog0("start channel scan of \"%s\".", _tuners[tuner]->name());\r
1263 \r
1264     // lock\r
1265     RaymLock(this);\r
1266 \r
1267     // 設定ファイルから全チューナ情報取得\r
1268     Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
1269     if (tunersInfo == NULL)\r
1270     {\r
1271         // 無ければ作成\r
1272         tunersInfo = Dictionary::dictionaryWithCapacity(0);\r
1273         _props->setObject(tunersInfo, KEY_TUNERS);\r
1274     }\r
1275 \r
1276     // 全チューナ情報から指定のチューナ情報を取得\r
1277     Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
1278     if (tunerInfo == NULL)\r
1279     {\r
1280         // 無ければ作成\r
1281         tunerInfo = Dictionary::dictionaryWithCapacity(0);\r
1282         tunersInfo->setObject(tunerInfo, _tuners[tuner]->name());\r
1283     }\r
1284 \r
1285     // チューナを未初期化に設定\r
1286     tunerInfo->setBool(false, KEY_INITIALIZED);\r
1287 \r
1288     // unlock\r
1289     RaymUnlock(this);\r
1290 \r
1291     // 全チャンネル情報を作成\r
1292     Dictionary *channels = Dictionary::dictionaryWithCapacity(0);\r
1293     tunerInfo->setObject(channels, KEY_CHANNELS);\r
1294 \r
1295     // チューナタイプにより最大チャンネル数を設定\r
1296     int max_channel;\r
1297     Tuner::Type type = _tuners[tuner]->type();\r
1298     if (type == Tuner::ISDB_S)\r
1299     {\r
1300         max_channel = Tuner::MAX_CHANNELS_ISDB_S;\r
1301     }\r
1302     else\r
1303     {\r
1304         max_channel = Tuner::MAX_CHANNELS_ISDB_T;\r
1305     }\r
1306 \r
1307     // 最終設定成功チャンネル保持用変数\r
1308     int lastChannel = -1;\r
1309 \r
1310     // チャンネルサーチ\r
1311     for (int ch = 0; ch <= max_channel; ++ch)\r
1312     {\r
1313         // チャンネル情報\r
1314         Dictionary *channelInfo = Dictionary::dictionaryWithCapacity(0);\r
1315 \r
1316         // チャンネルキー(10進数:PTに対するチャンネル番号)\r
1317         char chkey[4];\r
1318         sprintf_s(chkey, sizeof(chkey), "%03d", ch);\r
1319         channels->setObject(channelInfo, chkey);\r
1320 \r
1321         // チャンネルID(論理チャンネル番号)\r
1322         char channelID[8];\r
1323         if (type == Tuner::ISDB_S)\r
1324         {\r
1325             if (ch < 12)\r
1326             {\r
1327                 sprintf_s(channelID, sizeof(channelID), "BS%02d", 1 + 2 * ch);\r
1328             }\r
1329             else if (ch < 24)\r
1330             {\r
1331                 sprintf_s(channelID, sizeof(channelID), "ND%02d", 2 + 2 * (ch - 12));\r
1332             }\r
1333             else\r
1334             {\r
1335                 sprintf_s(channelID, sizeof(channelID), "ND%02d", 1 + 2 * (ch -24));\r
1336             }\r
1337         }\r
1338         else\r
1339         {\r
1340             static int TABLE[][3] =\r
1341             {\r
1342                 {   2,  0,   3 },\r
1343                 {  12,  1,  22 },\r
1344                 {  21,  0,  12 },\r
1345                 {  62,  1,  63 },\r
1346                 { 112,  0,  62 }\r
1347             };\r
1348 \r
1349             uint i;\r
1350             for (i = 0; i < sizeof(TABLE)/sizeof(*TABLE); ++i)\r
1351             {\r
1352                 if (ch <= TABLE[i][0])\r
1353                 {\r
1354                     sprintf_s(channelID, sizeof(channelID), "%s%d", TABLE[i][1] ? "C" : "", ch + TABLE[i][2] - TABLE[i][0]);\r
1355                     break;\r
1356                 }\r
1357             }\r
1358         }\r
1359 \r
1360         // チャンネルIDを設定\r
1361         channelInfo->setString(channelID, KEY_CHANNEL_ID);\r
1362 \r
1363         // lock\r
1364         RaymLock(this);\r
1365 \r
1366         // チャンネル設定\r
1367         if (_tuners[tuner]->setChannel(ch))\r
1368         {\r
1369             // 設定成功\r
1370             DebugLog0("  CH %s: OK", chkey);\r
1371 \r
1372             // チャンネルを有効に設定\r
1373             channelInfo->setBool(true, KEY_ENABLED);\r
1374 \r
1375             // 局情報を取得\r
1376             MPEG2::TS::Analyzer an;\r
1377             an.setFlag(MPEG2::TS::Analyzer::FLAG_SDT);\r
1378             _tuners[tuner]->setListener(&an);\r
1379             uint32_t count = 0;\r
1380             while (count++ < 8)\r
1381             {\r
1382                 Dictionary *stationInfo = an.stationInfo();\r
1383                 if (stationInfo != NULL)\r
1384                 {\r
1385                     if (stationInfo->stringForKey(KEY_NAME) != NULL)\r
1386                     {\r
1387                         channelInfo->setString(stationInfo->stringForKey(KEY_NAME), KEY_NAME);\r
1388                     }\r
1389                     if (stationInfo->objectForKey(KEY_SERVICES) != NULL)\r
1390                     {\r
1391                         channelInfo->setObject(stationInfo->objectForKey(KEY_SERVICES), KEY_SERVICES);\r
1392                     }\r
1393                 }\r
1394 \r
1395                 if ((channelInfo->stringForKey(KEY_NAME) != NULL) && (channelInfo->objectForKey(KEY_SERVICES) != NULL))\r
1396                 {\r
1397                     break;\r
1398                 }\r
1399             }\r
1400 \r
1401             if (channelInfo->stringForKey(KEY_NAME) != NULL)\r
1402             {\r
1403                 DebugLog0("    Name: %s", channelInfo->stringForKey(KEY_NAME)->cString());\r
1404             }\r
1405             _tuners[tuner]->setListener(NULL);\r
1406 \r
1407             // 成功したチャンネルを保持\r
1408             lastChannel = ch;\r
1409         }\r
1410         else\r
1411         {\r
1412             DebugLog0("  CH %s: NG", chkey);\r
1413         }\r
1414 \r
1415         // unlock\r
1416         RaymUnlock(this);\r
1417     }\r
1418 \r
1419     // lock\r
1420     RaymLock(this);\r
1421 \r
1422     // チューナを初期化済みに更新\r
1423     tunerInfo->setBool(true, KEY_INITIALIZED);\r
1424 \r
1425     // 設定が成功したチャンネルが有るか\r
1426     if (lastChannel >= 0)\r
1427     {\r
1428         // チューナを有効に設定\r
1429         tunerInfo->setBool(true, KEY_ENABLED);\r
1430 \r
1431         // 最終チャンネルをステータスファイルに設定\r
1432         Dictionary *tunersInfo = _status->dictionaryForKey(KEY_TUNERS);\r
1433         if (tunersInfo == NULL)\r
1434         {\r
1435             tunersInfo = Dictionary::dictionaryWithCapacity(0);\r
1436             _status->setObject(tunersInfo, KEY_TUNERS);\r
1437         }\r
1438 \r
1439         Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
1440         if (tunerInfo == NULL)\r
1441         {\r
1442             tunerInfo = Dictionary::dictionaryWithCapacity(0);\r
1443             tunersInfo->setObject(tunerInfo, _tuners[tuner]->name());\r
1444         }\r
1445 \r
1446         tunerInfo->setInteger(lastChannel, KEY_CHANNEL);\r
1447 \r
1448         _status->writeToFile(_status_path, false);\r
1449     }\r
1450 \r
1451     // 設定ファイルを更新\r
1452     _props->writeToFile(_props_path, true);\r
1453 \r
1454     // unlock\r
1455     RaymUnlock(this);\r
1456 }\r
1457 \r
1458 int Controller::getChannel(int tuner)\r
1459 {\r
1460     DebugLog2("Controller::getChannel()");\r
1461 \r
1462     int channel = -1;\r
1463 \r
1464     if ((0 <= tuner) && (tuner < _tunerCount))\r
1465     {\r
1466         // lock\r
1467         RaymLock(this);\r
1468 \r
1469         Dictionary *tunersInfo = _status->dictionaryForKey(KEY_TUNERS);\r
1470         if (tunersInfo != NULL)\r
1471         {\r
1472             Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
1473             if (tunerInfo != NULL)\r
1474             {\r
1475                 channel = tunerInfo->integerForKey(KEY_CHANNEL);\r
1476             }\r
1477         }\r
1478 \r
1479         // unlock\r
1480         RaymUnlock(this);\r
1481     }\r
1482 \r
1483     return channel;\r
1484 }\r
1485 \r
1486 bool Controller::setChannel(int tuner, int channel)\r
1487 {\r
1488     DebugLog2("Controller::setChannel()");\r
1489 \r
1490     bool result = false;\r
1491 \r
1492     if ((0 <= tuner) && (tuner < _tunerCount))\r
1493     {\r
1494         // lock\r
1495         RaymLock(this);\r
1496 \r
1497         if (!_tuners[tuner]->isLocked())\r
1498         {\r
1499             Dictionary *tunersInfo = _status->dictionaryForKey(KEY_TUNERS);\r
1500             if (tunersInfo == NULL)\r
1501             {\r
1502                 tunersInfo = Dictionary::dictionaryWithCapacity(0);\r
1503                 _status->setObject(tunersInfo, KEY_TUNERS);\r
1504             }\r
1505 \r
1506             Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
1507             if (tunerInfo == NULL)\r
1508             {\r
1509                 tunerInfo = Dictionary::dictionaryWithCapacity(0);\r
1510                 tunersInfo->setObject(tunerInfo, _tuners[tuner]->name());\r
1511             }\r
1512 \r
1513             if (channel != _tuners[tuner]->channel())\r
1514             {\r
1515                 if (_tuners[tuner]->setChannel(channel))\r
1516                 {\r
1517                     tunerInfo->setInteger(channel, KEY_CHANNEL);\r
1518 \r
1519                     _status->writeToFile(_status_path, false);\r
1520 \r
1521                     result = true;\r
1522                 }\r
1523                 else\r
1524                 {\r
1525                     DebugLog0("set channel failed. %d, %d", tuner, channel);\r
1526                 }\r
1527             }\r
1528         }\r
1529 \r
1530         // unlock\r
1531         RaymUnlock(this);\r
1532     }\r
1533 \r
1534     return result;\r
1535 }\r
1536 \r
1537 //\r
1538 // チューナ(streaming)制御/システム関連 周期処理\r
1539 //\r
1540 void Controller::periodic_2(void)\r
1541 {\r
1542     //\r
1543     // UDPポート監視\r
1544     //\r
1545 \r
1546     // マッピング(UDPPort:tuner,ch)情報取得\r
1547     Dictionary *mapping = NULL;\r
1548     if ((_streaming_ctrls != NULL) && ((mapping = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL)) != NULL))\r
1549     {\r
1550         // マッピング情報取得OK\r
1551 \r
1552         // 使用中のUDPの情報を取得\r
1553         // どれだけ使用中なのか不明なので、まずは必要サイズを調べる\r
1554         DWORD size = 0;\r
1555         if (GetExtendedUdpTable(NULL, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == ERROR_INSUFFICIENT_BUFFER)\r
1556         {\r
1557             // ERROR_INSUFFICIENT_BUFFER の場合、必要なバッファサイズが size に格納される\r
1558 \r
1559             // バッファ確保\r
1560             PMIB_UDPTABLE_OWNER_PID udptable = (PMIB_UDPTABLE_OWNER_PID)malloc(size);\r
1561             if (udptable != NULL)\r
1562             {\r
1563                 // バッファ確保OK\r
1564 \r
1565                 // UDP情報取得\r
1566                 if (GetExtendedUdpTable(udptable, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == NO_ERROR)\r
1567                 {\r
1568                     // 取得OK\r
1569                     DebugLog3("udptable->dwNumEntries: %d", udptable->dwNumEntries);\r
1570 \r
1571                     // 停止要否確認\r
1572                     Dictionary *using_port = _streaming_ctrls->dictionaryForKey(KEY_UDP_IN_USE);\r
1573                     if (using_port != NULL)\r
1574                     {\r
1575                         // key = 使用中ポート\r
1576                         Array *using_ports = using_port->allKeys();\r
1577                         if (using_ports != NULL)\r
1578                         {\r
1579                             // 使用中ポートでループ\r
1580                             for (uint i = 0; i < using_ports->count(); ++i)\r
1581                             {\r
1582                                 // 停止要否フラグ\r
1583                                 bool stop_need = true;\r
1584 \r
1585                                 // 使用中のUDP情報でループ\r
1586                                 for (uint j = 0; j < udptable->dwNumEntries; ++j)\r
1587                                 {\r
1588                                     if (((String *)using_ports->objectAtIndex(i))->intValue() == ntohs((WORD)udptable->table[j].dwLocalPort))\r
1589                                     {\r
1590                                         // 使用中なので停止不要\r
1591                                         stop_need = false;\r
1592                                         break;\r
1593                                     }\r
1594                                 }\r
1595 \r
1596                                 // 停止要否\r
1597                                 if (stop_need)\r
1598                                 {\r
1599                                     // マッピング情報を取得\r
1600                                     String *tuner_and_channel = mapping->stringForKey((String *)using_ports->objectAtIndex(i));\r
1601                                     if (tuner_and_channel != NULL)\r
1602                                     {\r
1603                                          // チューナとチャンネルに分割\r
1604                                         Range r = tuner_and_channel->rangeOfString(",");\r
1605                                         if (r.location != NotFound)\r
1606                                         {\r
1607                                             int tuner = tuner_and_channel->substringToIndex(r.location)->intValue();\r
1608                                             int channel = tuner_and_channel->substringFromIndex(r.location + 1)->intValue();\r
1609                                             DebugLog3("tuner: %d, channel: %d", tuner, channel);\r
1610 \r
1611                                             DebugLog0("auto streaming stop: %s", ((String *)using_ports->objectAtIndex(i))->cString());\r
1612 \r
1613                                             _tuners[tuner]->stopStreaming();\r
1614                                             using_port->removeObjectForKey((String *)using_ports->objectAtIndex(i));\r
1615                                         }\r
1616                                     }\r
1617                                 }\r
1618                             }\r
1619                         }\r
1620                     }\r
1621 \r
1622 \r
1623                     // 起動要否確認\r
1624                     for (uint i = 0; i < udptable->dwNumEntries; ++i)\r
1625                     {\r
1626                         // ポート番号を文字列に変換して\r
1627                         char port[10];\r
1628                         sprintf_s(port, "%d", ntohs((WORD)udptable->table[i].dwLocalPort));\r
1629                         DebugLog3("port = %s", port);\r
1630 \r
1631                         // マッピング情報を取得\r
1632                         String *tuner_and_channel = mapping->stringForKey(port);\r
1633                         if (tuner_and_channel != NULL)\r
1634                         {\r
1635                             // 取得OK: 監視対象ポートが使用されている\r
1636 \r
1637                             // 使用アプリを調べる\r
1638                             bool auto_streaming = false;\r
1639                             char exec_path[MAX_PATH];\r
1640                             memset(exec_path, 0, sizeof(exec_path));\r
1641 \r
1642                             // プロセスハンドル取得\r
1643                             size_t returnValue;\r
1644                             HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, udptable->table[i].dwOwningPid);\r
1645                             if (hProcess != NULL)\r
1646                             {\r
1647                                 TCHAR exec[MAX_PATH];\r
1648                                 memset(exec, 0, sizeof(exec));\r
1649                                 DWORD len = sizeof(exec) - 1;\r
1650 \r
1651                                 // イメージ取得\r
1652                                 if (QueryFullProcessImageName(hProcess, 0, exec, &len))\r
1653                                 {\r
1654                                     // ワイド -> マルチ 変換\r
1655                                     if (wcstombs_s(&returnValue, exec_path, sizeof(exec_path), exec, _TRUNCATE) == 0)\r
1656                                     {\r
1657                                         // 成功\r
1658 \r
1659                                         // とりあえず、、、現状は "ffmpeg.exe" / "vlc.exe" / "Kodi.exe" があったら auto_streaming を true にする\r
1660                                         if ((strstr(exec_path, "ffmpeg.exe") != NULL) ||\r
1661                                             (strstr(exec_path, "vlc.exe") != NULL) ||\r
1662                                             (strstr(exec_path, "Kodi.exe") != NULL))\r
1663                                         {\r
1664                                             auto_streaming = true;\r
1665                                         }\r
1666                                     }\r
1667                                 }\r
1668 \r
1669                                 // プロセスハンドル解放\r
1670                                 CloseHandle(hProcess);\r
1671                             }\r
1672 \r
1673                             if (auto_streaming)\r
1674                             {\r
1675                                 // チューナとチャンネルに分割\r
1676                                 Range r = tuner_and_channel->rangeOfString(",");\r
1677                                 if (r.location != NotFound)\r
1678                                 {\r
1679                                     int tuner = tuner_and_channel->substringToIndex(r.location)->intValue();\r
1680                                     int channel = tuner_and_channel->substringFromIndex(r.location + 1)->intValue();\r
1681                                     DebugLog3("tuner: %d, channel: %d", tuner, channel);\r
1682 \r
1683                                     // lock\r
1684                                     RaymLock(this);\r
1685 \r
1686                                     // 非ストリーミング中 かつ 非レコーディング中 または チャンネルが同じ 場合\r
1687                                     if (!_tuners[tuner]->isStreaming() && (!_tuners[tuner]->isRecording() || _tuners[tuner]->channel() == channel))\r
1688                                     {\r
1689                                         // ストリーミング開始可能\r
1690 \r
1691                                         if (_tuners[tuner]->channel() != channel)\r
1692                                         {\r
1693                                             setChannel(tuner, channel);\r
1694                                         }\r
1695 \r
1696                                         SOCKADDR_IN dst_addr;\r
1697                                         dst_addr.sin_family = AF_INET;\r
1698                                         dst_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r
1699                                         dst_addr.sin_port = (WORD)udptable->table[i].dwLocalPort;\r
1700 \r
1701                                         if (_tuners[tuner]->startStreaming(&dst_addr))\r
1702                                         {\r
1703                                             // 成功\r
1704                                             DebugLog0("auto streaming start: %d", ntohs((WORD)udptable->table[i].dwLocalPort));\r
1705 \r
1706                                             // 使用中ポートに登録\r
1707                                             using_port = _streaming_ctrls->dictionaryForKey(KEY_UDP_IN_USE);\r
1708                                             if (using_port == NULL)\r
1709                                             {\r
1710                                                 using_port = Dictionary::dictionaryWithCapacity(0);\r
1711                                                 _streaming_ctrls->setObject(using_port, KEY_UDP_IN_USE);\r
1712                                             }\r
1713                                             using_port->setBool(true, port);\r
1714                                         }\r
1715                                     }\r
1716 \r
1717                                     // unlock\r
1718                                     RaymUnlock(this);\r
1719                                 }\r
1720                             }\r
1721                         }\r
1722                     }\r
1723                 }\r
1724 \r
1725                 // バッファ解放\r
1726                 free(udptable);\r
1727             }\r
1728         }\r
1729     }\r
1730 \r
1731 \r
1732 \r
1733     //\r
1734     // 1/100秒単位が 0 に近くなるように次回T.O.を微調整\r
1735     //\r
1736 \r
1737 #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)\r
1738     static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64;\r
1739 #else\r
1740     static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL;\r
1741 #endif\r
1742     // 現在時刻を取得\r
1743     FILETIME ft;\r
1744     GetSystemTimeAsFileTime(&ft);\r
1745 \r
1746     // EPOCH秒への変換\r
1747     __time64_t now_sec;\r
1748     __time64_t now_usec;\r
1749     now_sec = ft.dwHighDateTime;\r
1750     now_sec <<= 32;\r
1751     now_sec |= ft.dwLowDateTime;\r
1752     now_sec /= 10;  /*convert into microseconds*/\r
1753     now_sec -= DELTA_EPOCH_IN_MICROSECS;\r
1754     now_usec = (now_sec % 1000000UL);\r
1755     now_sec = now_sec / 1000000UL;\r
1756 \r
1757     TimeInterval interval = (TimeInterval)now_usec;\r
1758     interval = interval / 1000000;\r
1759     _timer_periodic_2->setTimeInterval(1.005 - interval);\r
1760 }\r
1761 \r
1762 #ifndef _WIN32\r
1763 #pragma mark '\r
1764 #pragma mark ------- HTTP制御 -------\r
1765 #endif\r
1766 \r
1767 static std::string epg_regist_form(Dictionary *epg)\r
1768 {\r
1769     DebugLog3("epg_regist_form() start.");\r
1770 \r
1771     std::string epgs;\r
1772 \r
1773     if ((epg != NULL) &&\r
1774         (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
1775         (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
1776         (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
1777         (epg->stringForKey(KEY_EPG_END) != NULL) &&\r
1778         (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
1779         (epg->stringForKey(KEY_EPG_TITLE) != NULL))\r
1780     {\r
1781         epgs += "<form id=\"";\r
1782         epgs += "epg_";\r
1783         epgs += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
1784         epgs += "_";\r
1785         epgs += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
1786         epgs += "\" title=\"";\r
1787         epgs += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
1788         epgs += "-";\r
1789         epgs += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
1790         epgs += "\" class=\"panel\" action=\"regist.cgi\" method=\"GET\" target=\"_self\" onclick='return confirm(\"Is it ok?\");'>";\r
1791 \r
1792         // 放送時間\r
1793         epgs += "<h2>";\r
1794         epgs += LocalizedString(KEY_I18N_Broadcasting_Time, NULL)->cString();\r
1795         epgs += "</h2>";\r
1796         epgs += "<fieldset>";\r
1797         epgs += "<p class=\"normalText\">&nbsp;&nbsp;";\r
1798         epgs += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
1799         epgs += "&nbsp;";\r
1800         epgs += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
1801         epgs += "-";\r
1802         epgs += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
1803         epgs += "</p>";\r
1804         epgs += "</fieldset>";\r
1805 \r
1806         // 番組名\r
1807         epgs += "<h2>";\r
1808         epgs += LocalizedString(KEY_I18N_Program_Title, NULL)->cString();\r
1809         epgs += "</h2>";\r
1810         epgs += "<fieldset>";\r
1811         epgs += "<p class=\"normalText\">&nbsp;&nbsp;";\r
1812         epgs += epg->stringForKey(KEY_EPG_TITLE)->cString();\r
1813         epgs += "</p>";\r
1814         epgs += "</fieldset>";\r
1815 \r
1816         // 概要\r
1817         if (epg->stringForKey(KEY_EPG_DESCRIPTION) != NULL)\r
1818         {\r
1819             epgs += "<h2>";\r
1820             epgs += LocalizedString(KEY_I18N_Description, NULL)->cString();\r
1821             epgs += "</h2>";\r
1822             epgs += "<fieldset>";\r
1823             epgs += "<p class=\"normalText\">&nbsp;&nbsp;";\r
1824             epgs += epg->stringForKey(KEY_EPG_DESCRIPTION)->cString();\r
1825             epgs += "</p>";\r
1826             epgs += "</fieldset>";\r
1827         }\r
1828 \r
1829         //\r
1830         epgs += "<input type=\"hidden\" name=\"service_id\" value=\"";\r
1831         epgs += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
1832         epgs += "\"/>";\r
1833         epgs += "<input type=\"hidden\" name=\"event_id\" value=\"";\r
1834         epgs += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
1835         epgs += "\"/>";\r
1836         epgs += "<input class=\"redButton\" type=\"submit\" value=\"";\r
1837         epgs += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
1838         epgs += "\"/>";\r
1839     //    epgs += "<a class=\"redButton\" type=\"submit\">";\r
1840     //    epgs += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
1841     //    epgs += "</a>";\r
1842         epgs += "</form>";\r
1843     }\r
1844 \r
1845     return epgs;\r
1846 }\r
1847 \r
1848 HTTPResponse *responseWithDictionary(HTTPRequest *request, Dictionary *dictionary)\r
1849 {\r
1850     HTTPResponse *result = NULL;\r
1851     if ((request != NULL) && (dictionary != NULL))\r
1852     {\r
1853         std::string xml = dictionary->toString();\r
1854 \r
1855         // header\r
1856         InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
1857         // Date\r
1858         // Server\r
1859         // Content-Encoding\r
1860         // Last-Modified\r
1861         // Content-Type\r
1862         header->setFieldBodyWithName("application/xml", "Content-Type");\r
1863         // Connection\r
1864         // Tranfer-Encoding\r
1865         // Content-Length\r
1866         header->setFieldBodyWithName(String::stringWithFormat("%I64u", xml.length()), "Content-Length");\r
1867 \r
1868         // body\r
1869         InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(String::stringWithUTF8String(xml.c_str()));\r
1870 \r
1871         // message\r
1872         InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
1873         RELEASE(header);\r
1874         RELEASE(body);\r
1875         if (message != NULL)\r
1876         {\r
1877             result = HTTPResponse::alloc()->init();\r
1878             result->autorelease();\r
1879             result->setVersion(request->version());\r
1880             result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
1881             result->setStatus(200);\r
1882             result->setMessage(message);\r
1883             RELEASE(message);\r
1884         }\r
1885     }\r
1886     return result;\r
1887 }\r
1888 \r
1889 // positive response by XML\r
1890 HTTPResponse *responseForSuccess(HTTPRequest *request)\r
1891 {\r
1892     Dictionary *dict = Dictionary::dictionaryWithCapacity(0);\r
1893     dict->setString("Success", KEY_RESULT);\r
1894     return responseWithDictionary(request, dict);\r
1895 }\r
1896 \r
1897 // negative response by XML\r
1898 HTTPResponse *responseForFailed(HTTPRequest *request)\r
1899 {\r
1900     Dictionary *dict = Dictionary::dictionaryWithCapacity(0);\r
1901     dict->setString("Failed", KEY_RESULT);\r
1902     return responseWithDictionary(request, dict);\r
1903 }\r
1904 \r
1905 HTTPResponse *Controller::responseWithHTML(HTTPRequest *request, String *html)\r
1906 {\r
1907     HTTPResponse *result = NULL;\r
1908     if ((html != NULL) && (request != NULL))\r
1909     {\r
1910         // header\r
1911         InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
1912         // Date\r
1913         // Server\r
1914         // Content-Encoding\r
1915         // Last-Modified\r
1916         // Content-Type\r
1917         header->setFieldBodyWithName("text/html", "Content-Type");\r
1918         // Connection\r
1919         // Tranfer-Encoding\r
1920         // Content-Length\r
1921         header->setFieldBodyWithName(String::stringWithFormat("%I64u", html->length()), "Content-Length");\r
1922 \r
1923         // body\r
1924         InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(html);\r
1925 \r
1926         // message\r
1927         InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
1928         RELEASE(header);\r
1929         RELEASE(body);\r
1930         if (message != NULL)\r
1931         {\r
1932 //            result = HTTPResponse::response();\r
1933             result = HTTPResponse::alloc()->init();\r
1934             result->setVersion(request->version());\r
1935             result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
1936             result->setStatus(200);\r
1937             result->setMessage(message);\r
1938             result->autorelease();\r
1939             RELEASE(message);\r
1940         }\r
1941     }\r
1942     return result;\r
1943 }\r
1944 \r
1945 HTTPResponse *Controller::responseWithUTF8Text(HTTPRequest *request, String *text)\r
1946 {\r
1947     HTTPResponse *result = NULL;\r
1948     if ((text != NULL) && (request != NULL))\r
1949     {\r
1950         // header\r
1951         InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
1952         // Date\r
1953         // Server\r
1954         // Content-Encoding\r
1955         // Last-Modified\r
1956         // Content-Type\r
1957         header->setFieldBodyWithName("text/plane; charset=UTF-8", "Content-Type");\r
1958         // Connection\r
1959         // Tranfer-Encoding\r
1960         // Content-Length\r
1961         header->setFieldBodyWithName(String::stringWithFormat("%I64u", text->length()), "Content-Length");\r
1962 \r
1963         // body\r
1964         InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(text);\r
1965 \r
1966         // message\r
1967         InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
1968         RELEASE(header);\r
1969         RELEASE(body);\r
1970         if (message != NULL)\r
1971         {\r
1972 //            result = HTTPResponse::response();\r
1973             result = HTTPResponse::alloc()->init();\r
1974             result->setVersion(request->version());\r
1975             result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
1976             result->setStatus(200);\r
1977             result->setMessage(message);\r
1978             result->autorelease();\r
1979             RELEASE(message);\r
1980         }\r
1981     }\r
1982     return result;\r
1983 }\r
1984 \r
1985 HTTPResponse *Controller::responseByResultAndReferer(HTTPRequest *request, bool result, const char *referer)\r
1986 {\r
1987     HTTPResponse *retval = NULL;\r
1988 \r
1989     bool iui = false;\r
1990 \r
1991     String *ref = NULL;\r
1992     InternetTextMessage *msg = request->message();\r
1993     if (msg != NULL)\r
1994     {\r
1995         InternetTextMessageHeader *header = msg->header();\r
1996         if (header != NULL)\r
1997         {\r
1998             String *field_referer = header->fieldBodyForName("Referer");\r
1999             if (field_referer != NULL)\r
2000             {\r
2001                 String *field_host    = header->fieldBodyForName("Host");\r
2002                 if (field_host != NULL)\r
2003                 {\r
2004                     std::string tmp = "http://";\r
2005                     tmp += field_host->cString();\r
2006                     tmp += "/";\r
2007                     tmp += referer;\r
2008                     iui = field_referer->isEqualToString(tmp.c_str());\r
2009                     DebugLog2("tmp: %s", tmp.c_str());\r
2010                     DebugLog2("ref: %s", field_referer->cString());\r
2011                 }\r
2012             }\r
2013         }\r
2014     }\r
2015 //    if ((ref != NULL) && match(ref->cString(), referer))\r
2016 //    if ((ref != NULL) && ref->isEqualToString(referer))\r
2017     if (iui)\r
2018     {\r
2019         String *path = _httpd->rootPath()->stringByAppendingPathComponent("template2.html");\r
2020         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
2021         if (html != NULL)\r
2022         {\r
2023             html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", "Result");\r
2024             std::string contents;\r
2025             std::string reservations;\r
2026             contents += "<div id=\"home\" class=\"panel\" title=\"Result\" selected=\"true\">";\r
2027             if (result)\r
2028             {\r
2029                 contents += "<h2>Success</h2>";\r
2030             }\r
2031             else\r
2032             {\r
2033                 contents += "<h2>Failed</h2>";\r
2034             }\r
2035             contents += "</div>";\r
2036 \r
2037             html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
2038             retval = responseWithHTML(request, html);\r
2039         }\r
2040     }\r
2041     else\r
2042     {\r
2043         if (result)\r
2044         {\r
2045             retval = responseForSuccess(request);\r
2046         }\r
2047         else\r
2048         {\r
2049             retval = responseForFailed(request);\r
2050         }\r
2051     }\r
2052 \r
2053     return retval;\r
2054 }\r
2055 \r
2056 HTTPResponse *Controller::responseForMain(HTTPRequest *request, SOCKADDR_IN *client)\r
2057 {\r
2058     DebugLog2("Controller::responseForMain()");\r
2059 \r
2060     HTTPResponse *result = NULL;\r
2061     while ((request != NULL) && (client != NULL))\r
2062     {\r
2063         String *path = _httpd->rootPath();\r
2064         if (path == NULL)\r
2065         {\r
2066             DebugLog3("_httpd->rootPath() ng.");\r
2067             break;\r
2068         }\r
2069 \r
2070         path = path->stringByAppendingPathComponent("template1.html");\r
2071         if (path == NULL)\r
2072         {\r
2073             DebugLog3("path->stringByAppendingPathComponent() ng.");\r
2074             break;\r
2075         }\r
2076 \r
2077         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
2078         if (html == NULL)\r
2079         {\r
2080             DebugLog3("String::stringWithContentsOfFile() ng.");\r
2081             break;\r
2082         }\r
2083 \r
2084         String *server_name = _props->stringForKey(KEY_NAME);\r
2085         if (server_name == NULL)\r
2086         {\r
2087             DebugLog3("_props->stringForKey(KEY_NAME) ng.");\r
2088             break;\r
2089         }\r
2090 \r
2091         html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", server_name);\r
2092         if (html == NULL)\r
2093         {\r
2094             DebugLog3("html->stringByReplacingOccurrencesOfString() ng.");\r
2095             break;\r
2096         }\r
2097 \r
2098         html = html->stringByReplacingOccurrencesOfString("%%PAGE_TITLE%%", LocalizedString(KEY_I18N_Main_Menu, NULL));\r
2099         if (html == NULL)\r
2100         {\r
2101             DebugLog3("html->stringByReplacingOccurrencesOfString() ng.");\r
2102         }\r
2103 \r
2104         std::string contents;\r
2105         contents += "<div id=\"home\" class=\"panel\" selected=\"true\">";\r
2106         contents += "<ul>";\r
2107         contents += "<li><a target=\"_self\" href=\"/programs.html\">";\r
2108         contents += LocalizedString(KEY_I18N_Programs, NULL)->cString();\r
2109         contents += "</a></li>";\r
2110         contents += "<li><a target=\"_self\" href=\"/tv.html\">";\r
2111         contents += LocalizedString(KEY_I18N_TV, NULL)->cString();\r
2112         contents += "</a></li>";\r
2113         contents += "<li><a target=\"_self\" href=\"/video.html\">";\r
2114         contents += LocalizedString(KEY_I18N_Video, NULL)->cString();\r
2115         contents += "</a></li>";\r
2116         contents += "<li><a target=\"_self\" href=\"/reservation.html\">";\r
2117         contents += LocalizedString(KEY_I18N_Reservation, NULL)->cString();\r
2118         contents += "</a></li>";\r
2119         contents += "</ul>";\r
2120 #if 0\r
2121         contents += "<ul>";\r
2122         contents += "<li><a target=\"_self\" href=\"/exec_vlc.html\">VLC media player";\r
2123         contents += "</ul>";\r
2124 #endif\r
2125         contents += "<ul>";\r
2126         contents += "<li><a target=\"_self\" href=\"/status.html\">";\r
2127         contents += LocalizedString(KEY_I18N_Tuner_Status, NULL)->cString();\r
2128         contents += "</a></li>";\r
2129         contents += "</ul>";\r
2130         contents += "<center><a target=\"_self\" href=\"/iptd.log\">Ver. ";\r
2131         contents += VERSION;\r
2132         contents += "</a></center>";\r
2133         contents += "</div>";\r
2134 \r
2135         html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
2136         if (html != NULL)\r
2137         {\r
2138             result = responseWithHTML(request, html);\r
2139         }\r
2140 \r
2141         break;\r
2142     }\r
2143 \r
2144     return result;\r
2145 }\r
2146 \r
2147 HTTPResponse *Controller::responseForPrograms(HTTPRequest *request, SOCKADDR_IN *client)\r
2148 {\r
2149     DebugLog2("Controller::responseForPrograms() start.");\r
2150 \r
2151     HTTPResponse *result = NULL;\r
2152     while ((request != NULL) && (client != NULL))\r
2153     {\r
2154         String *path = _httpd->rootPath();\r
2155         if (path == NULL)\r
2156         {\r
2157             DebugLog3("_httpd->rootPath() ng.");\r
2158             break;\r
2159         }\r
2160 \r
2161         path = path->stringByAppendingPathComponent("template2.html");\r
2162         if (path == NULL)\r
2163         {\r
2164             DebugLog3("path->stringByAppendingPathComponent() ng.");\r
2165             break;\r
2166         }\r
2167 \r
2168         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
2169         if (html == NULL)\r
2170         {\r
2171             DebugLog3("String::stringWithContentsOfFile() ng.");\r
2172             break;\r
2173         }\r
2174 \r
2175         html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Programs, NULL));\r
2176         if (html == NULL)\r
2177         {\r
2178             DebugLog3("html->stringByReplacingOccurrencesOfString() ng.");\r
2179             break;\r
2180         }\r
2181 \r
2182         std::string contents;\r
2183         std::string epgs;\r
2184 \r
2185         contents += "<ul id=\"home\" title=\"";\r
2186         contents += LocalizedString(KEY_I18N_Programs, NULL)->cString();\r
2187         contents += "\" selected=\"true\">";\r
2188         // 地デジ\r
2189         contents += "<li>";\r
2190         contents += "<a href=\"#isdb_t\">";\r
2191         contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
2192         contents += "</a></li>";\r
2193         // BS/CS\r
2194         contents += "<li>";\r
2195         contents += "<a href=\"#isdb_s\">";\r
2196         contents += "BS/CS";\r
2197         contents += "</a></li>";\r
2198 \r
2199         contents += "</ul>";\r
2200 \r
2201         // 地デジ(局名)\r
2202         contents += "<ul id=\"isdb_t\" title=\"";\r
2203         contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
2204         contents += "\">";\r
2205         Array *stations = stationInfos(Tuner::ISDB_T);\r
2206         for (uint i = 0; i < stations->count(); ++i)\r
2207         {\r
2208             Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2209             if ((station != NULL) &&\r
2210                 (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2211                 (station->stringForKey(KEY_NAME) != NULL))\r
2212             {\r
2213                 contents += "<li>";\r
2214                 contents += "<a href=\"#isdb_t_";\r
2215                 contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
2216                 contents += "\">";\r
2217                 contents += station->stringForKey(KEY_NAME)->cString();\r
2218                 contents += "</a></li>";\r
2219             }\r
2220         }\r
2221         contents += "</ul>";\r
2222 \r
2223         // 地デジ(番組データ)\r
2224         for (uint i = 0; i < stations->count(); ++i)\r
2225         {\r
2226             Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2227             if ((station != NULL) &&\r
2228                 (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2229                 (station->stringForKey(KEY_NAME) != NULL))\r
2230             {\r
2231 \r
2232                 contents += "<ul id=\"isdb_t_";\r
2233                 contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
2234                 contents += "\" title=\"";\r
2235                 contents += station->stringForKey(KEY_NAME)->cString();\r
2236                 contents += "\">";\r
2237 \r
2238                 Array *programs = programsForServices(station->arrayForKey(KEY_SERVICES));\r
2239                 for (uint i = 0; i < programs->count(); ++i)\r
2240                 {\r
2241                     Dictionary *epg = (Dictionary *)programs->objectAtIndex(i);\r
2242                     if ((epg != NULL) &&\r
2243                         (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
2244                         (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
2245                         (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
2246                         (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
2247                         (epg->stringForKey(KEY_EPG_END) != NULL))\r
2248                     {\r
2249                         contents += "<li>";\r
2250                         contents += "<a href=\"#epg_";\r
2251                         contents += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
2252                         contents += "_";\r
2253                         contents += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
2254                         contents += "\">";\r
2255                         contents += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
2256                         contents += "&nbsp;";\r
2257                         contents += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
2258                         contents += "-";\r
2259                         contents += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
2260                         contents += "</a></li>";\r
2261 \r
2262                         epgs += epg_regist_form(epg);\r
2263                     }\r
2264                 }\r
2265                 contents += "</ul>";\r
2266             }\r
2267         }\r
2268 \r
2269         // BS/CS(局名)\r
2270         contents += "<ul id=\"isdb_s\" title=\"";\r
2271         contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
2272         contents += "\">";\r
2273         stations = stationInfos(Tuner::ISDB_S);\r
2274         for (uint i = 0; i < stations->count(); ++i)\r
2275         {\r
2276             Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2277             if ((station != NULL) &&\r
2278                 (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2279                 (station->stringForKey(KEY_NAME) != NULL))\r
2280             {\r
2281                 contents += "<li>";\r
2282                 contents += "<a href=\"#isdb_s_";\r
2283                 contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
2284                 contents += "\">";\r
2285                 contents += station->stringForKey(KEY_NAME)->cString();\r
2286                 contents += "</a></li>";\r
2287             }\r
2288         }\r
2289         contents += "</ul>";\r
2290 \r
2291         // BS/CS(番組データ)\r
2292         for (uint i = 0; i < stations->count(); ++i)\r
2293         {\r
2294             Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2295             if ((station != NULL) &&\r
2296                 (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2297                 (station->stringForKey(KEY_NAME) != NULL))\r
2298             {\r
2299                 contents += "<ul id=\"isdb_s_";\r
2300                 contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
2301                 contents += "\" title=\"";\r
2302                 contents += station->stringForKey(KEY_NAME)->cString();\r
2303                 contents += "\">";\r
2304 \r
2305                 Array *programs = programsForServices(station->arrayForKey(KEY_SERVICES));\r
2306                 for (uint i = 0; i < programs->count(); ++i)\r
2307                 {\r
2308                     Dictionary *epg = (Dictionary *)programs->objectAtIndex(i);\r
2309                     if ((epg != NULL) &&\r
2310                         (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
2311                         (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
2312                         (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
2313                         (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
2314                         (epg->stringForKey(KEY_EPG_END) != NULL))\r
2315                     {\r
2316                         contents += "<li>";\r
2317                         contents += "<a href=\"#epg_";\r
2318                         contents += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
2319                         contents += "_";\r
2320                         contents += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
2321                         contents += "\">";\r
2322                         contents += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
2323                         contents += "&nbsp;";\r
2324                         contents += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
2325                         contents += "-";\r
2326                         contents += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
2327                         contents += "</a></li>";\r
2328 \r
2329                         epgs += epg_regist_form(epg);\r
2330                     }\r
2331                 }\r
2332                 contents += "</ul>";\r
2333             }\r
2334         }\r
2335 \r
2336         contents += epgs;\r
2337         html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
2338         if (html != NULL)\r
2339         {\r
2340             result = responseWithHTML(request, html);\r
2341         }\r
2342 \r
2343         break;\r
2344     }    \r
2345     return result;\r
2346 }\r
2347 \r
2348 HTTPResponse *Controller::responseForReservation(HTTPRequest *request, SOCKADDR_IN *client)\r
2349 {\r
2350     DebugLog2("Controller::responseForReservation() start.");\r
2351 \r
2352     // lock\r
2353     RaymLock(this);\r
2354 \r
2355     HTTPResponse *result = NULL;\r
2356     while ((request != NULL) && (client != NULL))\r
2357     {\r
2358         // create array of reservations sorted by seq_id.\r
2359         Array *array = Array::arrayWithCapacity(0);\r
2360         for (int i = 0; i < _tunerCount; ++i)\r
2361         {\r
2362             Array *tmp = _reservations->arrayForKey(_tuners[i]->name());\r
2363             if (tmp == NULL)\r
2364             {\r
2365                 continue;\r
2366             }\r
2367             for (uint idx = 0; idx < tmp->count(); ++idx)\r
2368             {\r
2369                 Dictionary *epg = (Dictionary *)tmp->objectAtIndex(idx);\r
2370                 if (epg == NULL)\r
2371                 {\r
2372                     continue;\r
2373                 }\r
2374 \r
2375                 time_t epg_start;\r
2376                 time_t tmp_end;\r
2377                 getTimeWithEPG(epg, &epg_start, &tmp_end);\r
2378 \r
2379                 bool inserted = false;\r
2380                 for (uint j = 0; j < array->count(); ++j)\r
2381                 {\r
2382                     Dictionary *epg2 = (Dictionary *)array->objectAtIndex(j);\r
2383                     if (epg2 == NULL)\r
2384                     {\r
2385                         continue;\r
2386                     }\r
2387 \r
2388                     time_t epg2_start;\r
2389                     getTimeWithEPG(epg2, &epg2_start, &tmp_end);\r
2390 \r
2391                     if (epg_start <= epg2_start)\r
2392                     {\r
2393                         array->insertObject(epg, j);\r
2394                         inserted = true;\r
2395                         break;\r
2396                     }\r
2397                 }\r
2398 \r
2399                 if (!inserted)\r
2400                 {\r
2401                     array->addObject(epg);\r
2402                 }\r
2403             }\r
2404         }\r
2405 \r
2406         String *path = _httpd->rootPath();\r
2407         if (path == NULL)\r
2408         {\r
2409             break;\r
2410         }\r
2411 \r
2412         path = path->stringByAppendingPathComponent("template2.html");\r
2413         if (path == NULL)\r
2414         {\r
2415             break;\r
2416         }\r
2417 \r
2418         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
2419         if (html == NULL)\r
2420         {\r
2421             break;\r
2422         }\r
2423 \r
2424         html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Reservation, NULL));\r
2425         if (html == NULL)\r
2426         {\r
2427             break;\r
2428         }\r
2429 \r
2430         std::string contents;\r
2431         std::string reservations;\r
2432         contents += "<ul id=\"home\" title=\"";\r
2433         contents += LocalizedString(KEY_I18N_Reservation, NULL)->cString();\r
2434         contents += "\" selected=\"true\">";\r
2435 \r
2436 #if 0\r
2437         contents += "<li><a href=\"#tuners\">";\r
2438         contents += "<font color=\"blue\">[";\r
2439         contents += LocalizedString(KEY_I18N_Tuner, NULL)->cString();\r
2440         contents += "]</font>";\r
2441         contents += "</a></li>";\r
2442 #endif\r
2443 \r
2444         //\r
2445         // "[新規予約]"\r
2446         //\r
2447         contents += "<li><a href=\"#new_reservation\">";\r
2448         contents += "<font color=\"red\">[";\r
2449         contents += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
2450         contents += "]</font>";\r
2451         contents += "</a></li>";\r
2452 \r
2453         //\r
2454         // "[キーワード]"\r
2455         //\r
2456         contents += "<li><a href=\"#keywords\">";\r
2457         contents += "<font color=\"blue\">[";\r
2458         contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
2459         contents += "]</font>";\r
2460         contents += "</a></li>";\r
2461 \r
2462         //\r
2463         // 登録済み予約情報\r
2464         //\r
2465         for (uint i = 0; i < array->count(); ++i)\r
2466         {\r
2467             Dictionary *epg = (Dictionary *)array->objectAtIndex(i);\r
2468             char seq_id[7];\r
2469             sprintf_s(seq_id, "%06d", epg->integerForKey(KEY_EPG_RESV_ID) % 1000000);\r
2470             contents += "<li>";\r
2471             contents += "<a href=\"#seqid";\r
2472             contents += seq_id;\r
2473             contents += "\">";\r
2474             String *title = epg->stringForKey(KEY_EPG_TITLE);\r
2475             if (title != NULL)\r
2476             {\r
2477                 contents += title->cString();\r
2478             }\r
2479             else\r
2480             {\r
2481                 time_t start;\r
2482                 time_t end;\r
2483                 Controller::getTimeWithEPG(epg, &start, &end);\r
2484                 TM tm;\r
2485                 if (localtime_s(&tm, &start) == 0)\r
2486                 {\r
2487                     char date_time[24];\r
2488                     sprintf_s(date_time, "%04d/%02d/%02d %02d:%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);\r
2489                     contents += date_time;\r
2490                 }\r
2491                 else\r
2492                 {\r
2493                     contents += seq_id;\r
2494                 }\r
2495             }\r
2496             contents += "</a></li>";\r
2497 \r
2498             // reservations\r
2499             reservations += "<div id=\"";\r
2500             reservations += "seqid";\r
2501             reservations += seq_id;\r
2502             reservations += "\" title=\"";\r
2503             reservations += "xxxxxx";\r
2504             reservations += "\" class=\"panel\">";\r
2505 \r
2506             //reservations += "<h2>Status</h2>";\r
2507             reservations += "<fieldset>";\r
2508 \r
2509             //\r
2510             reservations += "<div class=\"row\">";\r
2511             reservations += "<label>Date</label>";\r
2512             reservations += "<span>";\r
2513             String *tmp = epg->stringForKey(KEY_EPG_DATE);\r
2514             reservations += tmp->cString();\r
2515             reservations += "</span>";\r
2516             reservations += "</div>";\r
2517 \r
2518             reservations += "<div class=\"row\">";\r
2519             reservations += "<label>Start</label>";\r
2520             reservations += "<span>";\r
2521             tmp = epg->stringForKey(KEY_EPG_START);\r
2522             reservations += tmp->cString();\r
2523             reservations += "</span>";\r
2524             reservations += "</div>";\r
2525 \r
2526             reservations += "<div class=\"row\">";\r
2527             reservations += "<label>End</label>";\r
2528             reservations += "<span>";\r
2529             tmp = epg->stringForKey(KEY_EPG_END);\r
2530             reservations += tmp->cString();\r
2531             reservations += "</span>";\r
2532             reservations += "</div>";\r
2533 \r
2534             reservations += "</fieldset>";\r
2535 \r
2536             reservations += "<a class=\"whiteButton\" type=\"submit\" href=\"/cancel.cgi?";\r
2537             reservations += "resv_id=";\r
2538             reservations += seq_id;\r
2539             reservations += "\" onclick='return confirm(\"To cancel. Is it ok?\");'>";\r
2540             reservations += LocalizedString(KEY_I18N_Cancel, NULL)->cString();\r
2541             reservations += "</a>";\r
2542 \r
2543             reservations += "</div>";\r
2544         }\r
2545 \r
2546         contents += "</ul>";\r
2547         contents += reservations;\r
2548 \r
2549         //\r
2550         // 新規予約:ここから\r
2551         //\r
2552 \r
2553         // 予約時間の初期値作成\r
2554         TM tm1, tm2;\r
2555         time_t time1 = time(NULL);\r
2556         time1 = (time1 + 15 * 60) / (15 * 60);\r
2557         time1 = time1 * 15 * 60;\r
2558         time_t time2 = time1 + 15 * 60;\r
2559         localtime_s(&tm1, &time1);\r
2560         localtime_s(&tm2, &time2);\r
2561         \r
2562         contents += "<form name=\"reservation\" id=\"new_reservation\" class=\"panel\" method=\"GET\" action=\"regist.cgi\" target=\"_self\">";\r
2563         contents += "<fieldset>";\r
2564         contents += "<div class=\"row\">";\r
2565         contents += "<label>";\r
2566         contents += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
2567         contents += " : </label>";\r
2568         contents += "<span>";\r
2569         contents += "<select name=\"service_id\" size=\"1\">";\r
2570 \r
2571         // 局情報の収集\r
2572         Array *stationInfos = Controller::stationInfos(Tuner::ISDB_T);\r
2573         if (stationInfos != NULL)\r
2574         {\r
2575             stationInfos->addObjectsFromArray(Controller::stationInfos(Tuner::ISDB_S));\r
2576         }\r
2577         else\r
2578         {\r
2579             stationInfos = Controller::stationInfos(Tuner::ISDB_S);\r
2580         }\r
2581         for (uint i = 0; (stationInfos != NULL) && (i < stationInfos->count()); ++i)\r
2582         {\r
2583             Array *services = ((Dictionary *)stationInfos->objectAtIndex(i))->arrayForKey(KEY_SERVICES);\r
2584 \r
2585 // とりえあず、現状は最初のサービスのみ表示\r
2586 //                for (uint j = 0; (services != NULL) &&  (j < services->count()); ++j)\r
2587             for (uint j = 0; (services != NULL) &&  (j < 1); ++j)\r
2588             {\r
2589                 Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
2590 \r
2591                 // テレビ放送かチェック\r
2592                 if ((service->stringForKey(KEY_SERVICE_TYPE) != NULL) && service->stringForKey(KEY_SERVICE_TYPE)->isEqualToString("1"))\r
2593                 {\r
2594                     String *name = service->stringForKey(KEY_NAME);\r
2595                     String *sid  = service->stringForKey(KEY_SERVICE_ID);\r
2596                     if ((name != NULL) && (sid != NULL))\r
2597                     {\r
2598                         String *tmp = String::stringWithFormat("<option value=\"%s\">%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
2599                         contents += tmp->cString();\r
2600                     }\r
2601                 }\r
2602             }\r
2603         }\r
2604 \r
2605         contents += "</select>";\r
2606         contents += "</span>";\r
2607         contents += "</div>";\r
2608         contents += "<div class=\"row\">";\r
2609         contents += "<label>";\r
2610         contents += LocalizedString(KEY_I18N_Date, NULL)->cString();\r
2611         contents += " : </label>";\r
2612         contents += "<span>";\r
2613         contents += "<select name=\"year\" size=\"1\">";\r
2614         for (int year = tm1.tm_year + 1900; year < tm1.tm_year + 1900 + 3; ++year)\r
2615         {\r
2616             char year_buf[64];\r
2617             sprintf_s(year_buf, "<option value=\"%d\">%d</option>", year, year);\r
2618             contents += year_buf;\r
2619         }\r
2620         contents += "</select>";\r
2621         contents += "<select name=\"month\" size=\"1\">";\r
2622         for (int month = 1; month < 13; ++month)\r
2623         {\r
2624             char month_buf[64];\r
2625             if (month == tm1.tm_mon + 1)\r
2626             {\r
2627                 sprintf_s(month_buf, "<option value=\"%d\" selected>%d</option>", month, month);\r
2628             }\r
2629             else\r
2630             {\r
2631                 sprintf_s(month_buf, "<option value=\"%d\">%d</option>", month, month);\r
2632             }\r
2633             contents += month_buf;\r
2634         }\r
2635         contents += "</select>";\r
2636         contents += "<select name=\"day\" size=\"1\">";\r
2637         for (int day = 1; day < 32; ++day)\r
2638         {\r
2639             char day_buf[64];\r
2640             if (day == tm1.tm_mday)\r
2641             {\r
2642                 sprintf_s(day_buf, "<option value=\"%d\" selected>%d</option>", day, day);\r
2643             }\r
2644             else\r
2645             {\r
2646                 sprintf_s(day_buf, "<option value=\"%d\">%d</option>", day, day);\r
2647             }\r
2648             contents += day_buf;\r
2649         }\r
2650         contents += "</select>";\r
2651         contents += "</span>";\r
2652         contents += "</div>";\r
2653         contents += "<div class=\"row\">";\r
2654         contents += "<label>";\r
2655         contents += LocalizedString(KEY_I18N_Start_Time, NULL)->cString();\r
2656         contents += " : </label>";\r
2657         contents += "<span>";\r
2658         contents += "<select name=\"start_hour\" size=\"1\">";\r
2659         for (int sh = 0; sh < 24; ++sh)\r
2660         {\r
2661             char sh_buf[64];\r
2662             if (sh == tm1.tm_hour)\r
2663             {\r
2664                 sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
2665             }\r
2666             else\r
2667             {\r
2668                 sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
2669             }\r
2670             contents += sh_buf;\r
2671         }\r
2672         contents += "</select>";\r
2673         contents += "<select name=\"start_min\" size=\"1\">";\r
2674         for (int sm = 0; sm < 60; ++sm)\r
2675         {\r
2676             char sm_buf[64];\r
2677             if (sm == tm1.tm_min)\r
2678             {\r
2679                 sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
2680             }\r
2681             else\r
2682             {\r
2683                 sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
2684             }\r
2685             contents += sm_buf;\r
2686         }\r
2687         contents += "</select>";\r
2688         contents += "</span>";\r
2689         contents += "</div>";\r
2690         contents += "<div class=\"row\">";\r
2691         contents += "<label>";\r
2692         contents += LocalizedString(KEY_I18N_End_Time, NULL)->cString();\r
2693         contents += " : </label>";\r
2694         contents += "<span>";\r
2695         contents += "<select name=\"end_hour\" size=\"1\">";\r
2696         for (int eh = 0; eh < 24; ++eh)\r
2697         {\r
2698             char eh_buf[64];\r
2699             if (eh == tm2.tm_hour)\r
2700             {\r
2701                 sprintf_s(eh_buf, "<option value=\"%02d\" selected>%02d</option>", eh, eh);\r
2702             }\r
2703             else\r
2704             {\r
2705                 sprintf_s(eh_buf, "<option value=\"%02d\">%02d</option>", eh, eh);\r
2706             }\r
2707             contents += eh_buf;\r
2708         }\r
2709         contents += "</select>";\r
2710         contents += "<select name=\"end_min\" size=\"1\">";\r
2711         for (int em = 0; em < 60; ++em)\r
2712         {\r
2713             char em_buf[64];\r
2714             if (em == tm2.tm_min)\r
2715             {\r
2716                 sprintf_s(em_buf, "<option value=\"%02d\" selected>%02d</option>", em, em);\r
2717             }\r
2718             else\r
2719             {\r
2720                 sprintf_s(em_buf, "<option value=\"%02d\">%02d</option>", em, em);\r
2721             }\r
2722             contents += em_buf;\r
2723         }\r
2724         contents += "</select>";\r
2725         contents += "</span>";\r
2726         contents += "</div>";\r
2727         contents += "<div class=\"row\">";\r
2728         contents += "<label>";\r
2729         contents += LocalizedString(KEY_I18N_Repeat, NULL)->cString();\r
2730         contents += " : </label>";\r
2731         contents += "<span>";\r
2732         contents += "<select name=\"repeat\" size=\"1\">";\r
2733         contents += "<option value=\"off\" selected>";\r
2734         contents += LocalizedString(KEY_I18N_Repeat_off, NULL)->cString();\r
2735         contents += "</option>";\r
2736         contents += "<option value=\"everyday\">";\r
2737         contents += LocalizedString(KEY_I18N_Repeat_everyday, NULL)->cString();\r
2738         contents += "</option>";\r
2739         contents += "<option value=\"weekly\">";\r
2740         contents += LocalizedString(KEY_I18N_Repeat_weekly, NULL)->cString();\r
2741         contents += "</option>";\r
2742         contents += "<option value=\"weekday\">";\r
2743         contents += LocalizedString(KEY_I18N_Repeat_weekday, NULL)->cString();\r
2744         contents += "</option>";\r
2745         contents += "</select>";\r
2746         contents += "</span>";\r
2747         contents += "</div>";\r
2748         contents += "</fieldset>";\r
2749 \r
2750         contents += "<input type=\"submit\" class=\"whiteButton\" value=\"";\r
2751         contents += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
2752         contents += "\">";\r
2753         contents += "</form>";\r
2754 \r
2755         //\r
2756         // 新規予約:ここまで\r
2757         //\r
2758 \r
2759 \r
2760         //\r
2761         // チューナ:ここから\r
2762         //\r
2763         contents += "<ul id=\"tuners\" title=\"";\r
2764         contents += LocalizedString(KEY_I18N_Tuner, NULL)->cString();\r
2765         contents += "\">";\r
2766 \r
2767         std::string controls;\r
2768         Dictionary *tunerInfos = _props->dictionaryForKey(KEY_TUNERS);\r
2769         if (tunerInfos != NULL)\r
2770         {\r
2771             for (int i = 0; i < _tunerCount; ++i)\r
2772             {\r
2773                 Dictionary *tunerInfo = tunerInfos->dictionaryForKey(_tuners[i]->name());\r
2774                 if (tunerInfo != NULL)\r
2775                 {\r
2776                     if (tunerInfo->boolForKey(KEY_INITIALIZED) && tunerInfo->boolForKey(KEY_ENABLED))\r
2777                     {\r
2778                         char key[4];\r
2779                         sprintf_s(key, "%03d", i);\r
2780 \r
2781                         // tuner list\r
2782                         contents += "<li>";\r
2783                         contents += "<a href=\"#tuner";\r
2784                         contents += key;\r
2785                         contents += "\">";\r
2786                         contents += key;\r
2787                         contents += ": ";\r
2788                         Dictionary *dict = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
2789                         if (dict != NULL)\r
2790                         {\r
2791                             char chkey[4];\r
2792                             sprintf_s(chkey, "%03d", tunerInfo->integerForKey(KEY_CHANNEL));\r
2793                             dict = dict->dictionaryForKey(chkey);\r
2794                             String *name = dict->stringForKey(KEY_NAME);\r
2795                             if (name != NULL)\r
2796                             {\r
2797                                 contents += name->cString();\r
2798                             }\r
2799                             else\r
2800                             {\r
2801                                 dict = NULL;\r
2802                             }\r
2803                         }\r
2804                         if (dict == NULL)\r
2805                         {\r
2806                             contents += _tuners[i]->name();\r
2807                         }\r
2808                         contents += "</a></li>";\r
2809 \r
2810                         // controls\r
2811                         controls += "<form name=\"control\" id=\"tuner";\r
2812                         controls += key;\r
2813                         controls += "\" class=\"panel\" method=\"GET\" action=\"/";\r
2814                         controls += key;\r
2815                         controls += "/recording=on\" target=\"_self\">";\r
2816 \r
2817                         // 白い枠:ここから\r
2818                         controls += "<fieldset>";\r
2819 \r
2820                         // 局指定:ここから\r
2821                         controls += "<div class=\"row\">";\r
2822                         controls += "<label>";\r
2823                         controls += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
2824                         controls += " : </label>";\r
2825                         controls += "<span>";\r
2826                         controls += "<select name=\"channel\" size=\"1\">";\r
2827                         Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
2828                         if (channels != NULL)\r
2829                         {\r
2830                             for (int ch = 0; ch <= Tuner::MAX_CHANNELS_ISDB_T; ++ch)\r
2831                             {\r
2832                                 char chkey[4];\r
2833                                 sprintf_s(chkey, "%03d", ch);\r
2834                                 dict = channels->dictionaryForKey(chkey);\r
2835                                 if (dict == NULL)\r
2836                                 {\r
2837                                     break;\r
2838                                 }\r
2839                                 Array *services = (Array *)dict->objectForKey(KEY_SERVICES);\r
2840                                 if (services != NULL)\r
2841                                 {\r
2842                                     for (uint s = 0; s < services->count(); ++s)\r
2843                                     {\r
2844                                         Dictionary *service = (Dictionary *)services->objectAtIndex(s);\r
2845                                         if (service != NULL)\r
2846                                         {\r
2847                                             char sid[8];\r
2848                                             sprintf_s(sid, "%d", service->integerForKey(KEY_SERVICE_ID));\r
2849                                             controls += "<option value=\"";\r
2850                                             controls += chkey;\r
2851                                             controls += "\"";\r
2852                                             // selected\r
2853                                             controls += ">";\r
2854                                             String *name = service->stringForKey(KEY_NAME);\r
2855                                             if (name != NULL)\r
2856                                             {\r
2857                                                 controls += name->cString();\r
2858                                             }\r
2859                                             controls += "[";\r
2860                                             controls += sid;\r
2861                                             controls += "]</option>";\r
2862                                         }\r
2863                                     }\r
2864                                 }\r
2865                             }\r
2866                         }\r
2867                         controls += "</select>";\r
2868                         controls += "</span>";\r
2869                         controls += "</div>";\r
2870                         // 局指定:ここまで\r
2871 \r
2872                         // 時間指定:ここから\r
2873                         controls += "<div class=\"row\">";\r
2874                         controls += "<label>";\r
2875                         controls += LocalizedString(KEY_I18N_Time, NULL)->cString();\r
2876                         controls += " : </label>";\r
2877                         controls += "<span>";\r
2878                         controls += "<select name=\"hour\" size=\"1\">";\r
2879                         for (int sh = 0; sh < 24; ++sh)\r
2880                         {\r
2881                             char sh_buf[64];\r
2882                             if (sh == 1)\r
2883                             {\r
2884                                 sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
2885                             }\r
2886                             else\r
2887                             {\r
2888                                 sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
2889                             }\r
2890                             controls += sh_buf;\r
2891                         }\r
2892                         controls += "</select>";\r
2893                         controls += "<select name=\"min\" size=\"1\">";\r
2894                         for (int sm = 0; sm < 60; ++sm)\r
2895                         {\r
2896                             char sm_buf[64];\r
2897                             if (sm == 0)\r
2898                             {\r
2899                                 sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
2900                             }\r
2901                             else\r
2902                             {\r
2903                                 sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
2904                             }\r
2905                             controls += sm_buf;\r
2906                         }\r
2907                         controls += "</select>";\r
2908                         controls += "</span>";\r
2909                         controls += "</div>";\r
2910                         // 時間指定:ここまで\r
2911 \r
2912                         controls += "</fieldset>";\r
2913                         // 白い枠:ここまで\r
2914 \r
2915                         controls += "<input type=\"submit\" class=\"whiteButton\" value=\"";\r
2916                         controls += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
2917                         controls += "\">";\r
2918                         controls += "</form>";\r
2919                     }\r
2920                 }\r
2921             }\r
2922         }\r
2923         contents += "</ul>";\r
2924         contents += controls;\r
2925 \r
2926         //\r
2927         // チューナ:ここまで\r
2928         //\r
2929 \r
2930         //\r
2931         // キーワード:ここから\r
2932         //\r
2933         contents += "<ul id=\"keywords\" title=\"";\r
2934         contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
2935         contents += "\">";\r
2936 \r
2937         contents += "<li><a href=\"#dialogForm\">";\r
2938         contents += "<font color=\"red\">[";\r
2939 //        contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
2940         contents += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
2941         contents += "]</font>";\r
2942         contents += "</a></li>";\r
2943 \r
2944         // 登録済みキーワード\r
2945         Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
2946         if (keywords_info != NULL)\r
2947         {\r
2948             controls = "";\r
2949             Array *keys = keywords_info->allKeys();\r
2950             if (keys != NULL)\r
2951             {\r
2952                 for (uint i = 0; i < keys->count(); ++i)\r
2953                 {\r
2954                     Dictionary *kwd_inf = keywords_info->dictionaryForKey((String *)keys->objectAtIndex(i));\r
2955                     if (kwd_inf == NULL)\r
2956                     {\r
2957                         continue;\r
2958                     }\r
2959 \r
2960                     char tmp[32];\r
2961                     sprintf_s(tmp, "%d", i);\r
2962                     contents += "<li><a href=\"#keywords_";\r
2963                     contents += tmp;\r
2964                     contents += "\">";\r
2965                     contents += ((String *)keys->objectAtIndex(i))->cString();\r
2966                     contents += "</a></li>";\r
2967 \r
2968                     controls += "<form name=\"filter\" id=\"keywords_";\r
2969                     controls += tmp;\r
2970                     controls += "\" class=\"panel\" method=\"GET\" action=\"mod_keywords.cgi\" target=\"_self\">";\r
2971                     controls += "<input type=\"hidden\" name=\"keywords\" value=\"";\r
2972                     controls += ((String *)keys->objectAtIndex(i))->cString();\r
2973                     controls += "\">";\r
2974                     controls += "<fieldset>";\r
2975 \r
2976                     controls += "<div class=\"row\">";\r
2977                     controls += "<label>";\r
2978                     controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
2979                     controls += " : </label>";\r
2980                     controls += "<span>";\r
2981                     controls += ((String *)keys->objectAtIndex(i))->cString();\r
2982                     controls += "</span>";\r
2983                     controls += "</div>";\r
2984 \r
2985                     controls += "<div class=\"row\">";\r
2986                     controls += "<label>";\r
2987                     controls += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
2988                     controls += " : </label>";\r
2989                     controls += "<span>";\r
2990                     controls += "<select name=\"service_id\" size=\"1\">";\r
2991                     controls += "<option value=\"-\">----</option>";\r
2992 \r
2993                     String *service_id = kwd_inf->stringForKey(KEY_EPG_SERVICE_ID);\r
2994 \r
2995                     // 局情報の収集\r
2996                     Array *stationInfos = Controller::stationInfos(Tuner::ISDB_T);\r
2997                     if (stationInfos != NULL)\r
2998                     {\r
2999                         stationInfos->addObjectsFromArray(Controller::stationInfos(Tuner::ISDB_S));\r
3000                     }\r
3001                     else\r
3002                     {\r
3003                         stationInfos = Controller::stationInfos(Tuner::ISDB_S);\r
3004                     }\r
3005                     for (uint i = 0; (stationInfos != NULL) && (i < stationInfos->count()); ++i)\r
3006                     {\r
3007                         Array *services = ((Dictionary *)stationInfos->objectAtIndex(i))->arrayForKey(KEY_SERVICES);\r
3008 \r
3009                         for (uint j = 0; (services != NULL) &&  (j < 1); ++j)\r
3010                         {\r
3011                             Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
3012 \r
3013                             // テレビ放送かチェック\r
3014                             if ((service->stringForKey(KEY_SERVICE_TYPE) != NULL) && service->stringForKey(KEY_SERVICE_TYPE)->isEqualToString("1"))\r
3015                             {\r
3016                                 String *name = service->stringForKey(KEY_NAME);\r
3017                                 String *sid  = service->stringForKey(KEY_SERVICE_ID);\r
3018                                 if ((name != NULL) && (sid != NULL))\r
3019                                 {\r
3020                                     if ((service_id != NULL) && (service_id->isEqualToString(sid)))\r
3021                                     {\r
3022                                         String *tmp = String::stringWithFormat("<option value=\"%s\" selected>%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
3023                                         controls += tmp->cString();\r
3024                                     }\r
3025                                     else\r
3026                                     {\r
3027                                         String *tmp = String::stringWithFormat("<option value=\"%s\">%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
3028                                         controls += tmp->cString();\r
3029                                     }\r
3030                                 }\r
3031                             }\r
3032                         }\r
3033                     }\r
3034 \r
3035                     controls += "</select>";\r
3036                     controls += "</span>";\r
3037                     controls += "</div>";\r
3038 \r
3039                     controls += "<div class=\"row\">";\r
3040                     controls += "<label>";\r
3041                     controls += LocalizedString(KEY_I18N_Start_Time, NULL)->cString();\r
3042                     controls += " : </label>";\r
3043                     controls += "<span>";\r
3044                     controls += "<select name=\"start_hour\" size=\"1\">";\r
3045                     controls += "<option value=\"-\">--</option>";\r
3046                     String *sh_str = kwd_inf->stringForKey(KEY_EPG_START);\r
3047                     for (int sh = 0; sh < 24; ++sh)\r
3048                     {\r
3049                         char sh_buf[64];\r
3050                         if ((sh_str != NULL) && (sh_str->length() == 5) && (sh_str->substringToIndex(2)->intValue() == sh))\r
3051                         {\r
3052                             sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
3053                         }\r
3054                         else\r
3055                         {\r
3056                             sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
3057                         }\r
3058                         controls += sh_buf;\r
3059                     }\r
3060                     controls += "</select>";\r
3061                     controls += "<select name=\"start_min\" size=\"1\">";\r
3062                     controls += "<option value=\"-\">--</option>";\r
3063                     for (int sm = 0; sm < 60; ++sm)\r
3064                     {\r
3065                         char sm_buf[64];\r
3066                         if ((sh_str != NULL) && (sh_str->length() == 5) && (sh_str->substringFromIndex(3)->intValue() == sm))\r
3067                         {\r
3068                             sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
3069                         }\r
3070                         else\r
3071                         {\r
3072                             sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
3073                         }\r
3074                         controls += sm_buf;\r
3075                     }\r
3076                     controls += "</select>";\r
3077                     controls += "</span>";\r
3078                     controls += "</div>";\r
3079 \r
3080                     controls += "</fieldset>";\r
3081 \r
3082                     controls += "<input type=\"submit\" class=\"whiteButton\" name=\"req_mod\" value=\"";\r
3083                     controls += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
3084                     controls += "\">";\r
3085 \r
3086                     controls += "<input type=\"submit\" class=\"redButton\" name=\"req_del\" value=\"";\r
3087                     controls += LocalizedString(KEY_I18N_Delete, NULL)->cString();\r
3088 //                    controls += "\">";\r
3089                     controls += "\" onclick='return confirm(\"To delete. Is it ok?\");'>";\r
3090 \r
3091                     controls += "</form>";\r
3092                 }\r
3093             }\r
3094         }\r
3095 \r
3096         contents += "</ul>";\r
3097         contents += controls;\r
3098 \r
3099         controls = "<form id=\"dialogForm\" title=\"";\r
3100         controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
3101         controls += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
3102         controls += "\" class=\"dialog\" target=\"_self\" action=\"add_keywords.cgi\" method=\"GET\">";\r
3103         controls += "<fieldset>";\r
3104         controls += "<h1>";\r
3105         controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
3106         controls += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
3107         controls += "</h1>";\r
3108         controls += "<a class=\"button leftButton\" type=\"cancel\">Cancel</a>";\r
3109         controls += "<a class=\"button blueButton\" type=\"submit\">Submit</a>";\r
3110         controls += "<label>Parm1:</label>";\r
3111         controls += "<input type=\"text\" name=\"keywords\" value=\"";\r
3112 //        controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
3113         controls += "\"/>";\r
3114         controls += "</fieldset>";\r
3115         controls += "<div class=\"spinner\"></div>";\r
3116         controls += "</form>";\r
3117 \r
3118         contents += controls;\r
3119         //\r
3120         // キーワード:ここまで\r
3121         //\r
3122 \r
3123         html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
3124         if (html != NULL)\r
3125         {\r
3126             result = responseWithHTML(request, html);\r
3127         }\r
3128 \r
3129         break;\r
3130     }\r
3131 \r
3132     // unlock\r
3133     RaymUnlock(this);\r
3134 \r
3135     return result;\r
3136 }\r
3137 \r
3138 HTTPResponse *Controller::responseForStatus(HTTPRequest *request, SOCKADDR_IN *client)\r
3139 {\r
3140     HTTPResponse *result = NULL;\r
3141     if ((request != NULL) && (client != NULL))\r
3142     {\r
3143         String *path = _httpd->rootPath()->stringByAppendingPathComponent("template2.html");\r
3144         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
3145         if (html != NULL)\r
3146         {\r
3147             html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Tuner_Status, NULL));\r
3148             std::string contents;\r
3149             std::string status;\r
3150             contents += "<ul id=\"home\" title=\"";\r
3151             contents += LocalizedString(KEY_I18N_Tuner_Status, NULL)->cString();\r
3152             contents += "\" selected=\"true\">";\r
3153             DebugLog2("_tunerCount = %d", _tunerCount);\r
3154             for (int i = 0; i < _tunerCount; ++i)\r
3155             {\r
3156                 char key[4];\r
3157                 sprintf_s(key, sizeof(key), "%03d", i);\r
3158 \r
3159                 if (isTunerInitialized(i))\r
3160                 {\r
3161                     contents += "<li>";\r
3162                     contents += "<a href=\"#tuner";\r
3163                     contents += key;\r
3164                     contents += "\">";\r
3165                     contents += key;\r
3166                     contents += ": ";\r
3167                     contents += _tuners[i]->name();\r
3168                     contents += "</a></li>";\r
3169 \r
3170                     //\r
3171                     status += "<div id=\"";\r
3172                     status += "tuner";\r
3173                     status += key;\r
3174                     status += "\" title=\"";\r
3175                     status += _tuners[i]->name();\r
3176                     status += "\" class=\"panel\">";\r
3177 \r
3178                     status += "<h2>Status</h2>";\r
3179                     status += "<fieldset>";\r
3180 \r
3181                     // Tuner Type\r
3182                     status += "<div class=\"row\">";\r
3183                     status += "<label>Type</label>";\r
3184                     status += "<span>";\r
3185                     switch (_tuners[i]->type())\r
3186                     {\r
3187                     case Tuner::ISDB_S:\r
3188                         status += "ISDB-S";\r
3189                         break;\r
3190                     case Tuner::ISDB_T:\r
3191                         status += "ISDB-T";\r
3192                         break;\r
3193                     case Tuner::TYPE_NA:\r
3194                     default:\r
3195                         status += "N/A";\r
3196                         break;\r
3197                     }\r
3198                     status += "</span>";\r
3199                     status += "</div>";\r
3200 \r
3201                     // LnbPower\r
3202                     status += "<div class=\"row\">";\r
3203                     status += "<label>LnbPower</label>";\r
3204                     status += "<span>";\r
3205                     switch (_tuners[i]->lnbPower())\r
3206                     {\r
3207                     case Tuner::LNB_POWER_11V:\r
3208                         status += "11V";\r
3209                         break;\r
3210                     case Tuner::LNB_POWER_15V:\r
3211                         status += "15V";\r
3212                         break;\r
3213                     case Tuner::LNB_POWER_OFF:\r
3214                     default:\r
3215                         status += "OFF";\r
3216                         break;\r
3217                     }\r
3218                     status += "</span>";\r
3219                     status += "</div>";\r
3220 \r
3221                     // Channel\r
3222                     char tmpstr[32];\r
3223                     status += "<div class=\"row\">";\r
3224                     status += "<label>Channel</label>";\r
3225                     status += "<span>";\r
3226                     sprintf_s(tmpstr, sizeof(tmpstr), "%03d", _tuners[i]->channel());\r
3227                     status += tmpstr;\r
3228                     status += "</span>";\r
3229                     status += "</div>";\r
3230 \r
3231                     // C/N[dB] AGC xxx/255\r
3232                     uint32_t cn100 = 0;\r
3233                     uint32_t agc = 0;\r
3234                     uint32_t maxAgc = 0;\r
3235                     _tuners[i]->getCnAgc(&cn100, &agc, &maxAgc);\r
3236 \r
3237                     sprintf_s(tmpstr, sizeof(tmpstr), "%d.%02d[dB]", cn100 / 100, cn100 % 100);\r
3238 \r
3239                     status += "<div class=\"row\">";\r
3240                     status += "<label>C/N</label>";\r
3241                     status += "<span>";\r
3242                     status += tmpstr;\r
3243                     status += "</span>";\r
3244                     status += "</div>";\r
3245 \r
3246                     sprintf_s(tmpstr, sizeof(tmpstr), "%03d/%03d", agc, maxAgc);\r
3247 \r
3248                     status += "<div class=\"row\">";\r
3249                     status += "<label>AGC</label>";\r
3250                     status += "<span>";\r
3251                     status += tmpstr;\r
3252                     status += "</span>";\r
3253                     status += "</div>";\r
3254 \r
3255                     status += "</fieldset>";\r
3256                     status += "</div>";\r
3257 \r
3258                 }\r
3259                 else\r
3260                 {\r
3261                     contents += "<li>";\r
3262                     contents += _tuners[i]->name();\r
3263                     contents += "[uninitialized]";\r
3264                     contents += "</li>";\r
3265                 }\r
3266             }\r
3267             contents += "</ul>";\r
3268             contents += status;\r
3269             html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
3270             result = responseWithHTML(request, html);\r
3271         }\r
3272         else\r
3273         {\r
3274             DebugLog2("responseForStatus() html is null\n");\r
3275         }\r
3276     }\r
3277     return result;\r
3278 }\r
3279 \r
3280 HTTPResponse *Controller::responseForRegistCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
3281 {\r
3282     DebugLog2("Controller::responseForRegistCGI()");\r
3283 \r
3284     HTTPResponse *result = NULL;\r
3285 \r
3286     // CGIリクエストとして解析\r
3287     Dictionary *cgi = request->parseAsCGI();\r
3288     if (cgi != NULL)\r
3289     {\r
3290         // CGIパスが一致しているか\r
3291         if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/regist.cgi")))\r
3292         {\r
3293             // パラメータがあるか\r
3294             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
3295             if (params != NULL)\r
3296             {\r
3297                 // パラメータ数が2か\r
3298                 if (params->count() == 2)\r
3299                 {\r
3300                     // パラメータのチェック\r
3301                     String *service_id = NULL;\r
3302                     String *event_id = NULL;\r
3303 \r
3304                     for (uint i = 0; i < params->count(); ++i)\r
3305                     {\r
3306                         Dictionary *param = (Dictionary *)params->objectAtIndex(i);\r
3307                         String *value = param->stringForKey("service_id");\r
3308                         if ((value != NULL) && value->isMatch("^\\d+$"))\r
3309                         {\r
3310                             service_id = value;\r
3311                         }\r
3312                         value = param->stringForKey("event_id");\r
3313                         if ((value != NULL) && value->isMatch("^\\d+$"))\r
3314                         {\r
3315                             event_id = value;\r
3316                         }\r
3317                     }\r
3318 \r
3319                     // 有効なパラメータか\r
3320                     if ((service_id != NULL) && (event_id != NULL))\r
3321                     {\r
3322                         DebugLog2("valid request");\r
3323 \r
3324                         result = responseByResultAndReferer(request, reserve(service_id->intValue(), event_id->intValue()), URI_PROGRAMS_HTML);\r
3325                     }\r
3326                 }\r
3327 \r
3328                 // パラメータ数が9か\r
3329                 else if (params->count() == 9)\r
3330                 {\r
3331                     // パラメータのチェック\r
3332                     String *service_id = NULL;\r
3333                     String *year       = NULL;\r
3334                     String *month      = NULL;\r
3335                     String *day        = NULL;\r
3336                     String *start_hour = NULL;\r
3337                     String *start_min  = NULL;\r
3338                     String *end_hour   = NULL;\r
3339                     String *end_min    = NULL;\r
3340                     String *repeat     = NULL;\r
3341 \r
3342                     struct {\r
3343                         const char *name;\r
3344                         String **variable;\r
3345                         const char *regex;\r
3346                     }\r
3347                     cgi[] =\r
3348                     {\r
3349                         {"service_id", &service_id, "^\\d+$"},\r
3350                         {"year",       &year,       "^\\d{4}$"},\r
3351                         {"month",      &month,      "^([1-9]|1[0-2])$"},\r
3352                         {"day",        &day,        "^([1-9]|[12][0-9]|3[01])$"},\r
3353                         {"start_hour", &start_hour, "^\\d{2}$"},\r
3354                         {"start_min",  &start_min,  "^\\d{2}$"},\r
3355                         {"end_hour",   &end_hour,   "^\\d{2}$"},\r
3356                         {"end_min",    &end_min,    "^\\d{2}$"},\r
3357                         {"repeat",     &repeat,     "^(off|everyday|weekly|weekday)$"},\r
3358                         {NULL, NULL, NULL}\r
3359                     };\r
3360 \r
3361                     for (uint i = 0; cgi[i].name != NULL; ++i)\r
3362                     {\r
3363                         for (uint j = 0; j < params->count(); ++j)\r
3364                         {\r
3365                             Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
3366                             String *value = param->stringForKey(cgi[i].name);\r
3367                             if ((value != NULL) && value->isMatch(cgi[i].regex))\r
3368                             {\r
3369                                 *(cgi[i].variable) = value;\r
3370                                 break;\r
3371                             }\r
3372                         }\r
3373                     }\r
3374 \r
3375                     // 有効なパラメータか\r
3376                     if ((service_id != NULL) && (year != NULL) && (month != NULL) && (day != NULL) &&\r
3377                         (start_hour != NULL) && (start_min != NULL) && (end_hour != NULL) && (end_min != NULL) && (repeat != NULL))\r
3378                     {\r
3379                         //\r
3380                         DebugLog1("valid param");\r
3381 \r
3382                         Dictionary *epg = Dictionary::dictionaryWithCapacity(0);\r
3383 \r
3384                         // 日付\r
3385                         epg->setString(String::stringWithFormat("%s/%02d/%02d", year->cString(), month->intValue(), day->intValue()), KEY_EPG_DATE);\r
3386 \r
3387                         // 開始時刻\r
3388                         epg->setString(String::stringWithFormat("%s:%s:00", start_hour->cString(), start_min->cString()), KEY_EPG_START);\r
3389 \r
3390                         // 終了時刻\r
3391                         epg->setString(String::stringWithFormat("%s:%s:00", end_hour->cString(), end_min->cString()), KEY_EPG_END);\r
3392 \r
3393                         // 繰り返し\r
3394                         epg->setString(repeat, KEY_EPG_REPEAT);\r
3395 \r
3396                         // Service ID\r
3397                         epg->setString(service_id, KEY_EPG_SERVICE_ID);\r
3398 \r
3399                         // Status\r
3400                         epg->setString("ready", KEY_EPG_STATUS);\r
3401 \r
3402                         result = responseByResultAndReferer(request, reserve(epg), URI_RESERVATION_HTML);\r
3403                     }\r
3404                 }\r
3405             }\r
3406         }\r
3407     }\r
3408 \r
3409     return result;\r
3410 }\r
3411 \r
3412 HTTPResponse *Controller::responseForCancelCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
3413 {\r
3414     DebugLog2("Controller::responseForCancelCGI()");\r
3415 \r
3416     HTTPResponse *result = NULL;\r
3417 \r
3418     // CGIリクエストとして解析\r
3419     Dictionary *cgi = request->parseAsCGI();\r
3420     if (cgi != NULL)\r
3421     {\r
3422         // CGIパスが一致しているか\r
3423         if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/cancel.cgi")))\r
3424         {\r
3425             // パラメータがあるか\r
3426             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
3427             if (params != NULL)\r
3428             {\r
3429                 // パラメータ数が1か\r
3430                 if (params->count() == 1)\r
3431                 {\r
3432                     Dictionary *param = (Dictionary *)params->objectAtIndex(0);\r
3433                     String *value = param->stringForKey("resv_id");\r
3434                     if ((value != NULL) && value->isMatch("^\\d{6}$"))\r
3435                     {\r
3436                         result = responseByResultAndReferer(request, cancel(-1, value->intValue()), URI_RESERVATION_HTML);\r
3437                     }\r
3438                 }\r
3439             }\r
3440         }\r
3441     }\r
3442     return result;\r
3443 }\r
3444 \r
3445 HTTPResponse *Controller::responseForAddKeywordsCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
3446 {\r
3447     DebugLog2("Controller::responseForAddKeywordsCGI()");\r
3448 \r
3449     HTTPResponse *result = NULL;\r
3450 \r
3451     // CGIリクエストとして解析\r
3452     Dictionary *cgi = request->parseAsCGI();\r
3453     if (cgi != NULL)\r
3454     {\r
3455         // CGIパスが一致しているか\r
3456         if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/add_keywords.cgi")))\r
3457         {\r
3458             // パラメータがあるか\r
3459             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
3460             if (params != NULL)\r
3461             {\r
3462                 // パラメータ数が1か\r
3463                 if (params->count() == 1)\r
3464                 {\r
3465                     Dictionary *param = (Dictionary *)params->objectAtIndex(0);\r
3466                     String *value = param->stringForKey("keywords");\r
3467                     if (value != NULL)\r
3468                     {\r
3469                         value = value->stringByReplacingOccurrencesOfString("+", " ");\r
3470                         if (value != NULL)\r
3471                         {\r
3472                             value = value->stringByRemovingPercentEncoding();\r
3473                         }\r
3474                     }\r
3475                     if (value != NULL)\r
3476                     {\r
3477                         // lock\r
3478                         RaymLock(this);\r
3479 \r
3480                         Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
3481                         if (keywords_info == NULL)\r
3482                         {\r
3483                             keywords_info = Dictionary::dictionaryWithCapacity(0);\r
3484                             _reservations->setObject(keywords_info, KEY_EPG_KEYWORDS);\r
3485                         }\r
3486 \r
3487                         if (keywords_info->dictionaryForKey(value) == NULL)\r
3488                         {\r
3489                             // 新規キーワードの場合\r
3490                             keywords_info->setObject(Dictionary::dictionaryWithCapacity(0), value);\r
3491 \r
3492                             // キーワード予約更新\r
3493                             updateKeywordsReservation();\r
3494 \r
3495                             // スケジュール更新\r
3496                             updateSchedule();\r
3497 \r
3498                             // 保存\r
3499                             _reservations->writeToFile(_reservations_path, true);\r
3500                         }\r
3501 \r
3502                         RaymUnlock(this);\r
3503 \r
3504                         result = responseForReloadURI(request, client, "/reservation.html#_keywords");\r
3505                     }\r
3506                 }\r
3507             }\r
3508         }\r
3509     }\r
3510     return result;\r
3511 }\r
3512 \r
3513 HTTPResponse *Controller::responseForModKeywordsCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
3514 {\r
3515     DebugLog2("Controller::responseForModKeywordsCGI()");\r
3516 \r
3517     HTTPResponse *result = NULL;\r
3518 \r
3519     // CGIリクエストとして解析\r
3520     Dictionary *cgi = request->parseAsCGI();\r
3521     if (cgi != NULL)\r
3522     {\r
3523         DebugLog3("cgi != NULL");\r
3524 \r
3525         // CGIパスが一致しているか\r
3526         if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/mod_keywords.cgi")))\r
3527         {\r
3528             DebugLog3("CGI path OK.");\r
3529 \r
3530             // パラメータがあるか\r
3531             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
3532             if (params != NULL)\r
3533             {\r
3534                 DebugLog3("params != NULL");\r
3535 \r
3536                 // パラメータのチェック\r
3537                 String *keywords   = NULL;\r
3538                 String *service_id = NULL;\r
3539                 String *start_hour = NULL;\r
3540                 String *start_min  = NULL;\r
3541                 String *req_mod    = NULL;\r
3542                 String *req_del    = NULL;\r
3543 \r
3544                 struct {\r
3545                     const char *name;\r
3546                     String **variable;\r
3547                     const char *regex;\r
3548                 }\r
3549                 cgi[] =\r
3550                 {\r
3551                     {"keywords",   &keywords,   ".*"},\r
3552                     {"service_id", &service_id, "^(\\d+|-)$"},\r
3553                     {"start_hour", &start_hour, "^(\\d{2}|-)$"},\r
3554                     {"start_min",  &start_min,  "^(\\d{2}|-)$"},\r
3555                     {"req_mod",    &req_mod,    ".*"},\r
3556                     {"req_del",    &req_del,    ".*"},\r
3557                     {NULL, NULL, NULL}\r
3558                 };\r
3559 \r
3560                 for (uint i = 0; cgi[i].name != NULL; ++i)\r
3561                 {\r
3562                     for (uint j = 0; j < params->count(); ++j)\r
3563                     {\r
3564                         Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
3565                         String *value = param->stringForKey(cgi[i].name);\r
3566                         if ((value != NULL) && value->isMatch(cgi[i].regex))\r
3567                         {\r
3568                             *(cgi[i].variable) = value;\r
3569                             break;\r
3570                         }\r
3571                     }\r
3572                 }\r
3573 \r
3574                 if (keywords != NULL)\r
3575                 {\r
3576                     keywords = keywords->stringByReplacingOccurrencesOfString("+", " ");\r
3577                     if (keywords != NULL)\r
3578                     {\r
3579                         keywords = keywords->stringByRemovingPercentEncoding();\r
3580                     }\r
3581                 }\r
3582 \r
3583                 if ((keywords != NULL) && (service_id != NULL) && (start_hour != NULL) && (start_min != NULL) &&\r
3584                     ((req_mod != NULL) && (req_del == NULL)) || ((req_mod == NULL) && (req_del != NULL)))\r
3585                 {\r
3586 \r
3587                     // lock\r
3588                     RaymLock(this);\r
3589 \r
3590                     Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
3591                     if (keywords_info != NULL)\r
3592                     {\r
3593                         if (req_mod != NULL)\r
3594                         {\r
3595                             DebugLog0("keywords: %s", keywords->cString());\r
3596                             DebugLog0("service_id: %s", service_id->cString());\r
3597                             DebugLog0("start_hour: %s", start_hour->cString());\r
3598                             DebugLog0("start_min: %s", start_min->cString());\r
3599                             Dictionary *kwd_inf = keywords_info->dictionaryForKey(keywords);\r
3600                             if (kwd_inf != NULL)\r
3601                             {\r
3602                                 if (!service_id->isEqualToString("-"))\r
3603                                 {\r
3604                                     kwd_inf->setString(service_id, KEY_EPG_SERVICE_ID);\r
3605                                 }\r
3606                                 else\r
3607                                 {\r
3608                                     kwd_inf->removeObjectForKey(KEY_EPG_SERVICE_ID);\r
3609                                 }\r
3610                                 if (!start_hour->isEqualToString("-") && !start_min->isEqualToString("-"))\r
3611                                 {\r
3612                                     kwd_inf->setString(String::stringWithFormat("%s:%s", start_hour->cString(), start_min->cString()), KEY_EPG_START);\r
3613                                 }\r
3614                                 else\r
3615                                 {\r
3616                                     kwd_inf->removeObjectForKey(KEY_EPG_START);\r
3617                                 }\r
3618                             }\r
3619                         }\r
3620                         else if (req_del != NULL)\r
3621                         {\r
3622                             DebugLog0("keywords: %s", keywords->cString());\r
3623                             keywords_info->removeObjectForKey(keywords);\r
3624                         }\r
3625 \r
3626                         // キーワード予約更新\r
3627                         updateKeywordsReservation();\r
3628 \r
3629                         // スケジュール更新\r
3630                         updateSchedule();\r
3631 \r
3632                         // 保存\r
3633                         _reservations->writeToFile(_reservations_path, true);\r
3634                     }\r
3635 \r
3636                     // unlock\r
3637                     RaymUnlock(this);\r
3638 \r
3639                     result = responseForReloadURI(request, client, "/reservation.html#_keywords");\r
3640                 }\r
3641             }\r
3642         }\r
3643     }\r
3644     return result;\r
3645 }\r
3646 \r
3647 HTTPResponse *Controller::responseForReloadURI(NET::HTTPRequest *request, SOCKADDR_IN *client, const char *uri, int sec)\r
3648 {\r
3649     HTTPResponse *result = NULL;\r
3650 \r
3651     if (uri != NULL)\r
3652     {\r
3653         std::string contents;\r
3654         contents = "<html>";\r
3655         contents += "<head>";\r
3656 #if 0\r
3657         contents += "<meta http-equiv=\"refresh\" content=\"0;URL=";\r
3658 #else\r
3659         contents += "<meta http-equiv=\"refresh\" content=\"";\r
3660         contents += sec;\r
3661         contents += ";URL=";\r
3662 #endif\r
3663         contents += uri;\r
3664         contents += "\">";\r
3665         contents += "</head>";\r
3666         contents += "</html>";\r
3667         String *html = String::stringWithUTF8String(contents.c_str());\r
3668         if (html != NULL)\r
3669         {\r
3670             result = responseWithHTML(request, html);\r
3671         }\r
3672     }\r
3673 \r
3674     return result;\r
3675 }\r
3676 \r
3677 HTTPResponse *Controller::responseForPlaylist(HTTPRequest *request, SOCKADDR_IN *client)\r
3678 {\r
3679     HTTPResponse *result = NULL;\r
3680     // http://bit.ly/iptv_feb2015\r
3681 \r
3682 #if 0\r
3683 \r
3684 #EXTM3U\r
3685 \r
3686 #EXTINF:-1, [COLOR yellow]Updated 15/04/2015 @ 03:45 [/COLOR]  \r
3687 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
3688 #EXTINF:-1, [COLOR green] --Uk Live Tv--[/COLOR]  \r
3689 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
3690 #EXTINF:-1, [COLOR green] --Free Collection of IPTV sports links--[/COLOR]  \r
3691 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
3692 #EXTINF:-1, [COLOR red] --Links will go down. Please note that it will take time to update--[/COLOR]\r
3693 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
3694 #EXTINF:-1, [COLOR red] --Please contact me at the husham.com website--[/COLOR]\r
3695 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
3696 \r
3697 #EXTINF:-1, Sky sports news\r
3698 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
3699 #EXTINF:-1,Bt sports 1\r
3700 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
3701 \r
3702 #endif\r
3703 \r
3704 \r
3705     std::string contents;\r
3706     contents = "#EXTM3U\r\n";\r
3707     contents += "\r\n";\r
3708 \r
3709     Dictionary *tuner_channel_to_udp = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
3710     if (tuner_channel_to_udp != NULL)\r
3711     {\r
3712         Tuner::Type types[] = {Tuner::ISDB_T, Tuner::ISDB_S};\r
3713         for (int t = 0; t < sizeof(types) / sizeof(Tuner::Type); ++t)\r
3714         {\r
3715             for (int i = 0; i < _tunerCount; ++i)\r
3716             {\r
3717                 if (isTunerInitialized(i) && (_tuners[i]->type() == types[t]))\r
3718                 {\r
3719                     uint ch_max = ((types[t] == Tuner::ISDB_T) ? Tuner::MAX_CHANNELS_ISDB_T : Tuner::MAX_CHANNELS_ISDB_S);\r
3720                     for (uint ch = 0; ch <= ch_max; ++ch)\r
3721                     {\r
3722                         if (isChannelEnabled(i, ch))\r
3723                         {\r
3724                             char tuner_and_channel[10];\r
3725                             sprintf_s(tuner_and_channel, "%d,%d", i, ch);\r
3726                             char *chstr = strchr(tuner_and_channel, ',');\r
3727                             if (chstr != NULL)\r
3728                             {\r
3729                                 ++chstr;\r
3730                             }\r
3731 \r
3732                             String *udp_str = tuner_channel_to_udp->stringForKey(tuner_and_channel);\r
3733                             if (udp_str != NULL)\r
3734                             {\r
3735                                 contents += "#EXTINF:-1 tvg-id=\"";\r
3736                                 contents += tuner_and_channel;\r
3737                                 contents += "\" tvg-logo=\"";\r
3738                                 contents += ((types[t] == Tuner::ISDB_T) ? "t_" : "s_");\r
3739                                 if (chstr != NULL)\r
3740                                 {\r
3741                                     contents += chstr;\r
3742                                 }\r
3743                                 else\r
3744                                 {\r
3745                                     contents += "ffff";\r
3746                                 }\r
3747                                 contents += "\" group-title=\"";\r
3748                                 contents += _tuners[i]->name();\r
3749                                 contents += "\", ";\r
3750                                 String *station_name = stationName(types[t], ch);\r
3751                                 if (station_name != NULL)\r
3752                                 {\r
3753                                     contents += station_name->cString();\r
3754                                 }\r
3755                                 else\r
3756                                 {\r
3757                                     contents += tuner_and_channel;\r
3758                                 }\r
3759                                 contents += "\r\n";\r
3760                                 contents += "udp://0.0.0.0:";\r
3761                                 contents += udp_str->cString();\r
3762                                 contents += "\r\n";\r
3763                             }\r
3764                         }\r
3765                     }\r
3766                 }\r
3767             }\r
3768         }\r
3769 \r
3770     }\r
3771 \r
3772     String *text = String::stringWithUTF8String(contents.c_str());\r
3773     if (text != NULL)\r
3774     {\r
3775         result = responseWithUTF8Text(request, text);\r
3776     }\r
3777 \r
3778     return result;\r
3779 }\r
3780 \r
3781 /**\r
3782  * @brief HTTP Live Streaming制御\r
3783  *\r
3784  *     http://hogehoge/チューナ番号/チャンネル番号/streaming[-プリセット名].m3u8 がリクエスト(プリセットはオプション)され、\r
3785  *     チューナ番号/チャンネル番号/プリセット名(ある場合)が有効値の場合にコールされる\r
3786  *\r
3787  * @param [in] request    HTTPリクエスト\r
3788  * @param [in] client     リクエストしたクライアントのアドレス\r
3789  * @param [in] tuner      チューナ番号\r
3790  * @param [in] channel    チャンネル番号\r
3791  * @param [in] preset     プリセット(URLで省略された場合 "default" )\r
3792  */\r
3793 HTTPResponse *Controller::responseForHLSControl(HTTPRequest *request, SOCKADDR_IN *client, int tuner, int channel, String *preset)\r
3794 {\r
3795     DebugLog0("Controller::responseForHLSControl()");\r
3796 \r
3797     HTTPResponse *result = NULL;\r
3798 \r
3799     // client からホスト名を取得\r
3800     char hostname[NI_MAXHOST];\r
3801     if (getnameinfo((SOCKADDR *)client, sizeof(SOCKADDR_IN), hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)\r
3802     {\r
3803         // 取得OK\r
3804         DebugLog0("host: %s", hostname);\r
3805 \r
3806         // ストリーミング制御情報からHLS情報を取得\r
3807         Dictionary *hls_info = _streaming_ctrls->dictionaryForKey(KEY_HLS_INFO);\r
3808         if (hls_info == NULL)\r
3809         {\r
3810             DebugLog0("hls_info == NULL");\r
3811             hls_info = Dictionary::dictionaryWithCapacity(0);\r
3812             _streaming_ctrls->setObject(hls_info, KEY_HLS_INFO);\r
3813         }\r
3814 \r
3815         // HLS情報から指定チューナの情報を取得\r
3816         Dictionary *hls_info_tuner = hls_info->dictionaryForKey(_tuners[tuner]->name());\r
3817         if (hls_info_tuner == NULL)\r
3818         {\r
3819             DebugLog0("hls_info_tuner == NULL");\r
3820             hls_info_tuner = Dictionary::dictionaryWithCapacity(0);\r
3821             hls_info->setObject(hls_info_tuner, _tuners[tuner]->name());\r
3822         }\r
3823 \r
3824         // 指定チューナの情報からホスト名を確認\r
3825         if ((hls_info_tuner->stringForKey(KEY_HOSTNAME) == NULL) || hls_info_tuner->stringForKey(KEY_HOSTNAME)->isEqualToString(hostname))\r
3826         {\r
3827             DebugLog0("new host or reconnect");\r
3828              // ホスト名を設定\r
3829             hls_info_tuner->setString(hostname, KEY_HOSTNAME);\r
3830 \r
3831             // HLS制御インスタンス取得\r
3832             HTTPLiveStreaming *hls = (HTTPLiveStreaming *)hls_info_tuner->objectForKey(KEY_HLS_INSTANCE);\r
3833             if (hls == NULL)\r
3834             {\r
3835                 DebugLog0("hls == NULL");\r
3836                 // 生成\r
3837                 hls = HTTPLiveStreaming::alloc()->init()->autorelease();\r
3838                 hls_info_tuner->setObject(hls, KEY_HLS_INSTANCE);\r
3839             }\r
3840 \r
3841             // 異チャンネルへのリクエスト、または、プリセット変更の場合\r
3842             if (((hls_info_tuner->integerForKey(KEY_CHANNEL) != 0) && (hls_info_tuner->integerForKey(KEY_CHANNEL) != channel)) ||\r
3843                 ((hls_info_tuner->stringForKey(KEY_PRESET) != NULL) && !hls_info_tuner->stringForKey(KEY_PRESET)->isEqualToString(preset)))\r
3844             {\r
3845                 DebugLog0("hls->stop()");\r
3846 \r
3847                 // 停止\r
3848                 hls->stop();\r
3849 \r
3850                 // チャンネルとプリセットは一旦削除\r
3851                 hls_info_tuner->removeObjectForKey(KEY_CHANNEL);\r
3852                 hls_info_tuner->removeObjectForKey(KEY_PRESET);\r
3853             }\r
3854 \r
3855             DebugLog0("start ?");\r
3856 \r
3857             // 初回リクエスト or チャンネル/プリセット変更 か?\r
3858             if ((hls_info_tuner->integerForKey(KEY_CHANNEL) == 0) && (hls_info_tuner->stringForKey(KEY_PRESET) == NULL))\r
3859             {\r
3860                 DebugLog0("start or restart");\r
3861 \r
3862                 // UDPポートを検索\r
3863                 Dictionary *mapping = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
3864                 if (mapping != NULL)\r
3865                 {\r
3866                     Array *ports = mapping->allKeys();\r
3867                     if (ports != NULL)\r
3868                     {\r
3869                         char tuner_and_channel[10];\r
3870                         sprintf_s(tuner_and_channel, "%d,%d", tuner, channel);\r
3871 \r
3872                         for (uint i = 0; i < ports->count(); ++i)\r
3873                         {\r
3874                             String *port = (String *)ports->objectAtIndex(i);\r
3875                             String *tmp = mapping->stringForKey(port);\r
3876                             if (tmp->isEqualToString(tuner_and_channel))\r
3877                             {\r
3878                                 DebugLog0("udp mapping %d -> %d,%d", port->intValue(), tuner, channel);\r
3879 \r
3880                                 // ソースを設定\r
3881                                 hls->setSource(String::stringWithFormat("udp://@:%d", port->intValue()));\r
3882 \r
3883                                 // トランスコード\r
3884 //                                    hls->setTranscode(_props->dictionaryForKey(KEY_PRESETS)->dictionaryForKey(preset));\r
3885 \r
3886                                 // 出力先を設定\r
3887                                 hls->setOutputPath(_props->stringForKey(KEY_CACHE_PATH));\r
3888 \r
3889                                 // インデックス名\r
3890                                 hls->setIndexName(String::stringWithFormat("live_%03d_%03d", tuner, channel));\r
3891 \r
3892                                 // 開始\r
3893                                 if (hls->start())\r
3894                                 {\r
3895                                     DebugLog0("hls->start() success");\r
3896 \r
3897                                     // チャンネルを保存\r
3898                                     hls_info_tuner->setInteger(0, KEY_CHANNEL);\r
3899 \r
3900                                     // プリセットを保存\r
3901                                     hls_info_tuner->setString(preset, KEY_PRESET);\r
3902                                 }\r
3903 \r
3904                                 break;\r
3905                             }\r
3906                         }\r
3907                     }\r
3908                 }\r
3909             }\r
3910 \r
3911 \r
3912             String *index_path = hls->indexPath();\r
3913             FileManager *fm = FileManager::defaultManager();\r
3914             bool isDirectory = false;\r
3915             if (fm->fileExistsAtPath(index_path, &isDirectory))\r
3916             {\r
3917                 if (!isDirectory)\r
3918                 {\r
3919                     DebugLog0("file exists");\r
3920                     result = _httpd->responseWithPath(index_path, request);\r
3921                 }\r
3922             }\r
3923             if (result == NULL)\r
3924             {\r
3925                 DebugLog0("file no exists");\r
3926                 result = responseForReloadURI(request, client, request->URI()->cString(), 10);\r
3927             }\r
3928 \r
3929 //            result = responseForFailed(request);\r
3930         }\r
3931         else\r
3932         {\r
3933             // 他ホストが使用中\r
3934         }\r
3935     }\r
3936 \r
3937     return result;\r
3938 }\r
3939 \r
3940 HTTPResponse *Controller::requestTunerControl(HTTPRequest *request, SOCKADDR_IN *client, int tuner)\r
3941 {\r
3942     DebugLog0("%s\n", __FUNCTION__);\r
3943 \r
3944     HTTPResponse *result = NULL;\r
3945 \r
3946     // lock\r
3947     RaymLock(this);\r
3948 \r
3949     // URI取得\r
3950     String *uri = request->URI();\r
3951     while (uri != NULL)\r
3952     {\r
3953         // CGIリクエストとして解析\r
3954         Dictionary *cgi = request->parseAsCGI();\r
3955         if (cgi != NULL)\r
3956         {\r
3957             uri = cgi->stringForKey(HTTPRequest::KEY_CGI);\r
3958             if (uri == NULL)\r
3959             {\r
3960                 break;\r
3961             }\r
3962         }\r
3963 \r
3964         //\r
3965         // チューナ情報\r
3966         //\r
3967         if (uri->isMatch("^/[0-9]{3}/info.xml$") && (cgi == NULL))\r
3968         {\r
3969         }\r
3970 \r
3971         //\r
3972         // チャンネル設定\r
3973         //   /ttt/channel=nnn\r
3974         //\r
3975         else if (uri->isMatch("^/[0-9]{3}/channel=[0-9]{1,3}$") && (cgi == NULL))\r
3976         {\r
3977 DebugLog0("ch set");\r
3978             String *ch = uri->substringFromIndex(13);\r
3979             if (ch == NULL)\r
3980             {\r
3981                 break;\r
3982             }\r
3983             int channel = ch->intValue();\r
3984 DebugLog0("set %d channel:%d(%s)\n", tuner, channel, ch->cString());\r
3985             DebugLog2("set channel:%d(%s)\n", channel, ch->cString());\r
3986             if (setChannel(tuner, channel))\r
3987             {\r
3988                 // success\r
3989                 DebugLog2("success.\n");\r
3990                 result = responseForSuccess(request);\r
3991             }\r
3992             else\r
3993             {\r
3994                 // failed\r
3995                 DebugLog2("failed.\n");\r
3996                 result = responseForFailed(request);\r
3997             }\r
3998         }\r
3999 \r
4000         //\r
4001         // 録画開始(最大23:59まで)\r
4002         //   /ttt/recording=on?hour=hh&min=mm[&channel=nnn]\r
4003         //\r
4004         else if (uri->isMatch("^/[0-9]{3}/recording=on$") && (cgi != NULL))\r
4005         {\r
4006             // パラメータがあるか\r
4007             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
4008             if (params == NULL)\r
4009             {\r
4010                 break;\r
4011             }\r
4012 \r
4013             // パラメータ数は2〜3か\r
4014             if ((params->count() != 2) && (params->count() != 3))\r
4015             {\r
4016                 break;\r
4017             }\r
4018 \r
4019             // パラメータのチェック\r
4020             String *p_hour    = NULL;\r
4021             String *p_min     = NULL;\r
4022             String *p_channel = NULL;\r
4023 \r
4024             struct {\r
4025                 const char *name;\r
4026                 String **variable;\r
4027                 const char *regex;\r
4028             }\r
4029             cgi[] =\r
4030             {\r
4031                 {"hour",    &p_hour,    "^[0-2][0-9]$"},\r
4032                 {"min",     &p_min,     "^[0-5][0-9]$"},\r
4033                 {"channel", &p_channel, "^[0-9]{3}$"},\r
4034                 {NULL, NULL, NULL}\r
4035             };\r
4036 \r
4037             for (uint i = 0; cgi[i].name != NULL; ++i)\r
4038             {\r
4039                 *(cgi[i].variable) = NULL;\r
4040                 for (uint j = 0; j < params->count(); ++j)\r
4041                 {\r
4042                     Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
4043                     String *value = param->stringForKey(cgi[i].name);\r
4044                     if ((value != NULL) && value->isMatch(cgi[i].regex))\r
4045                     {\r
4046                         *(cgi[i].variable) = value;\r
4047                     }\r
4048                 }\r
4049             }\r
4050 \r
4051             // パラメータは有効か\r
4052             if ((p_hour == NULL) || (p_min == NULL))\r
4053             {\r
4054                 break;\r
4055             }\r
4056 \r
4057             // チャンネル設定\r
4058             int channel = 0;\r
4059             if (p_channel != NULL)\r
4060             {\r
4061                 channel = p_channel->intValue();\r
4062             }\r
4063             else\r
4064             {\r
4065                 channel = _tuners[tuner]->channel();\r
4066             }\r
4067 \r
4068             if (channel >= 0)\r
4069             {\r
4070                 // recording on\r
4071                 int hour = p_hour->intValue();\r
4072                 int min = p_min->intValue();\r
4073                 if (hour < 24)\r
4074                 {\r
4075                     // EPG生成\r
4076                     Dictionary *epg = Dictionary::dictionaryWithCapacity(0);\r
4077                     while (true)\r
4078                     {\r
4079                         time_t now;\r
4080                         time(&now);\r
4081                         now += 1; // margin\r
4082                         TM tm;\r
4083                         if (localtime_s(&tm, &now) != 0)\r
4084                         {\r
4085                             epg = NULL;\r
4086                             break;\r
4087                         }\r
4088                         TM end;\r
4089                         end = tm;\r
4090                         end.tm_hour += hour;\r
4091                         end.tm_min += min;\r
4092                         end.tm_sec += 1; // margin\r
4093                         if (mktime(&end) == -1)\r
4094                         {\r
4095                             epg = NULL;\r
4096                             break;\r
4097                         }\r
4098 \r
4099                         char tmp[16];\r
4100 \r
4101                         // Date\r
4102                         sprintf_s(tmp, sizeof(tmp), "%04d/%02d/%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);\r
4103                         epg->setString(tmp, KEY_EPG_DATE);\r
4104 \r
4105                         // Start\r
4106                         sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);\r
4107                         epg->setString(tmp, KEY_EPG_START);\r
4108 \r
4109                         // End\r
4110                         sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", end.tm_hour, end.tm_min, end.tm_sec);\r
4111                         epg->setString(tmp, KEY_EPG_END);\r
4112 \r
4113                         // Channel\r
4114                         sprintf_s(tmp, sizeof(tmp), "%d", channel);\r
4115                         epg->setString(tmp, KEY_EPG_CHANNEL);\r
4116 \r
4117                         // 繰り返し\r
4118                         epg->setString("off", KEY_EPG_REPEAT); \r
4119 \r
4120                         // Status\r
4121                         epg->setString("ready", KEY_EPG_STATUS);\r
4122 \r
4123                         break;\r
4124                     }\r
4125                 \r
4126                     if (epg != NULL)\r
4127                     {\r
4128                         // 録画開始&結果生成\r
4129                         result = responseByResultAndReferer(request, reserve(tuner, epg), URI_RESERVATION_HTML);\r
4130                     }\r
4131                 }\r
4132             }\r
4133         }\r
4134 \r
4135         //\r
4136         // 録画停止\r
4137         //   /ttt/recording=off\r
4138         //\r
4139         else if (uri->isMatch("^/[0-9]{3}/recording=off$") && (cgi == NULL))\r
4140         {\r
4141             // recording off\r
4142             DebugLog2("recording off: %s\n", uri->cString());\r
4143             if (cancel(tuner, -1))\r
4144             {\r
4145                 // success\r
4146                 DebugLog2("success.\n");\r
4147                 result = responseForSuccess(request);\r
4148             }\r
4149             else\r
4150             {\r
4151                 // failed\r
4152                 DebugLog2("failed.\n");\r
4153                 result = responseForFailed(request);\r
4154             }\r
4155         }\r
4156 \r
4157         //\r
4158         // ストリーミング開始\r
4159         //   /ttt/streaming=on?udp=nnnnn(&host=aaaaaa)\r
4160         //\r
4161         else if (uri->isMatch("^/[0-9]{3}/streaming=on$") && (cgi != NULL))\r
4162         {\r
4163             // パラメータがあるか\r
4164             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
4165             if (params == NULL)\r
4166             {\r
4167                 break;\r
4168             }\r
4169 \r
4170             // パラメータ数は1〜2か\r
4171             if ((params->count() != 1) && (params->count() != 2))\r
4172             {\r
4173                 break;\r
4174             }\r
4175 \r
4176             // パラメータのチェック\r
4177             String *p_udp  = NULL;\r
4178             String *p_host = NULL;\r
4179 \r
4180             struct {\r
4181                 const char *name;\r
4182                 String **variable;\r
4183                 const char *regex;\r
4184             }\r
4185             cgi[] =\r
4186             {\r
4187                 {"udp",  &p_udp,  "^[0-9]{1,5}$"},\r
4188                 {"host", &p_host, "^.+$"},\r
4189                 {NULL, NULL, NULL}\r
4190             };\r
4191 \r
4192             for (uint i = 0; cgi[i].name != NULL; ++i)\r
4193             {\r
4194                 *(cgi[i].variable) = NULL;\r
4195                 for (uint j = 0; j < params->count(); ++j)\r
4196                 {\r
4197                     Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
4198                     String *value = param->stringForKey(cgi[i].name);\r
4199                     if ((value != NULL) && value->isMatch(cgi[i].regex))\r
4200                     {\r
4201                         *(cgi[i].variable) = value;\r
4202                     }\r
4203                 }\r
4204             }\r
4205 \r
4206             // パラメータチェック\r
4207             if (p_udp == NULL)\r
4208             {\r
4209                 break;\r
4210             }\r
4211 \r
4212             SOCKADDR_IN dst_addr;\r
4213             \r
4214             if (p_host != NULL)\r
4215             {\r
4216     #if 0\r
4217                 std::string host = udpstr.substr(idx + 5);\r
4218                 udpstr = udpstr.substr(0, idx - 1);\r
4219                 DebugLog2("udp: %s\n", udpstr.c_str());\r
4220                 DebugLog2("host: %s\n", host.c_str());\r
4221                 struct hostent *ent = gethostbyname(host.c_str());\r
4222     #endif\r
4223             }\r
4224             else\r
4225             {\r
4226                 memcpy(&dst_addr, client, sizeof(SOCKADDR_IN));\r
4227             }\r
4228             dst_addr.sin_port = htons(p_udp->intValue());\r
4229             \r
4230             if (_tuners[tuner]->startStreaming(&dst_addr))\r
4231             {\r
4232                 // success\r
4233                 DebugLog2("success.\n");\r
4234                 result = responseForSuccess(request);\r
4235             }\r
4236             else\r
4237             {\r
4238                 // failed\r
4239                 DebugLog2("failed.\n");\r
4240                 result = responseForFailed(request);\r
4241             }\r
4242         }\r
4243 \r
4244         //\r
4245         // ストリーミング停止\r
4246         //   /ttt/streaming=off(?host=aaaa)\r
4247         //\r
4248         else if (uri->isMatch("^/[0-9]{3}/streaming=off$"))\r
4249         {\r
4250             // パラメータ\r
4251             String *p_host = NULL;\r
4252 \r
4253             // パラメータがあるか\r
4254             if (cgi != NULL)\r
4255             {\r
4256                 Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
4257                 if (params == NULL)\r
4258                 {\r
4259                     break;\r
4260                 }\r
4261 \r
4262                 // パラメータ数は0〜1か\r
4263                 if ((params->count() != 0) && (params->count() != 1))\r
4264                 {\r
4265                     break;\r
4266                 }\r
4267 \r
4268                 struct {\r
4269                     const char *name;\r
4270                     String **variable;\r
4271                     const char *regex;\r
4272                 }\r
4273                 cgi[] =\r
4274                 {\r
4275                     {"host", &p_host, "^.+$"},\r
4276                     {NULL, NULL, NULL}\r
4277                 };\r
4278 \r
4279                 for (uint i = 0; cgi[i].name != NULL; ++i)\r
4280                 {\r
4281                     *(cgi[i].variable) = NULL;\r
4282                     for (uint j = 0; j < params->count(); ++j)\r
4283                     {\r
4284                         Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
4285                         String *value = param->stringForKey(cgi[i].name);\r
4286                         if ((value != NULL) && value->isMatch(cgi[i].regex))\r
4287                         {\r
4288                             *(cgi[i].variable) = value;\r
4289                         }\r
4290                     }\r
4291                 }\r
4292             }\r
4293 \r
4294             SOCKADDR_IN dst_addr;\r
4295             if (p_host != NULL)\r
4296             {\r
4297             }\r
4298             else\r
4299             {\r
4300             }\r
4301 \r
4302             _tuners[tuner]->stopStreaming();\r
4303             \r
4304             // success\r
4305             DebugLog2("success.\n");\r
4306             result = responseForSuccess(request);\r
4307         }\r
4308 \r
4309         //\r
4310         // HLS制御\r
4311         //\r
4312         else if (uri->isMatch("^/[0-9]{3}/[0-9]{3}/streaming(-[^\\.]+)?.m3u8$") && (cgi == NULL))\r
4313         {\r
4314             DebugLog0("uri: %s", uri->cString());\r
4315 \r
4316             // URIからチャンネル番号を抽出\r
4317             //   Range実装したい...\r
4318             int ch = uri->substringFromIndex(5)->substringToIndex(3)->intValue();\r
4319             DebugLog0("ch: %d", ch);\r
4320 \r
4321             // presetが指定されている場合、presetを抽出\r
4322             String *preset = NULL;\r
4323             if (uri->isMatch("streaming-"))\r
4324             {\r
4325                 preset = uri->substringFromIndex(19);\r
4326                 preset = preset->substringToIndex(preset->length() - 5);\r
4327                 DebugLog0("opt: preset: %s", preset->cString());\r
4328             }\r
4329             else\r
4330             {\r
4331                 // なければ "default"\r
4332                 preset = String::stringWithUTF8String(KEY_DEFAULT);\r
4333             }\r
4334 \r
4335             // チャンネル/presetが有効か確認\r
4336             if (isChannelEnabled(tuner, ch) &&\r
4337                 (_props->dictionaryForKey(KEY_PRESETS) != NULL) &&\r
4338                 (_props->dictionaryForKey(KEY_PRESETS)->objectForKey(preset) != NULL))\r
4339             {\r
4340                 // \r
4341                 result = responseForHLSControl(request, client, tuner, ch, preset);\r
4342             }\r
4343             else\r
4344             {\r
4345                 result = responseForFailed(request);\r
4346             }\r
4347             DebugLog0("hls req. done");\r
4348         }\r
4349         else if (uri->isMatch("^/[0-9]{3}/[0-9]{3}/streaming-[0-9]+.ts$") && (cgi == NULL))\r
4350         {\r
4351             // 分割されたTS\r
4352             DebugLog0("uri: %s", uri->cString());\r
4353         }\r
4354 \r
4355         break;\r
4356     }\r
4357 \r
4358     // unlock\r
4359     RaymUnlock(this);\r
4360 \r
4361     return result;\r
4362 }\r
4363 \r
4364 HTTPResponse *Controller::request(HTTPRequest *request, SOCKADDR_IN *client)\r
4365 {\r
4366     DebugLog2("%s\n", __FUNCTION__);\r
4367 \r
4368     // 初期化チェック\r
4369     bool flag = false;\r
4370     RaymLock(this);\r
4371     flag = _initialized;\r
4372     RaymUnlock(this);\r
4373     if (!flag)\r
4374     {\r
4375         return NULL;\r
4376     }\r
4377 \r
4378     HTTPResponse *response = NULL;\r
4379 \r
4380     if (request->method()->isEqualToString("GET") ||\r
4381         request->method()->isEqualToString("HEAD"))\r
4382     {\r
4383         // URI\r
4384         String *uri = request->URI();\r
4385         DebugLog0("request: %s\n", uri->cString());\r
4386 \r
4387         //\r
4388         //\r
4389         //\r
4390         if (uri->isMatch("^(/|/index\\.(htm|html))$"))\r
4391         {\r
4392             response = responseForMain(request, client);\r
4393         }\r
4394 \r
4395         else if (uri->isMatch("^/status.html$"))\r
4396         {\r
4397             response = responseForStatus(request, client);\r
4398         }\r
4399 \r
4400         //\r
4401         else if (uri->isMatch("^/info.xml$"))\r
4402         {\r
4403         }\r
4404         else if (uri->isMatch("^/video.xml$"))\r
4405         {\r
4406         }\r
4407 \r
4408         //\r
4409         else if (uri->isMatch("^/suspend.xml$"))\r
4410         {\r
4411             if (canSuspend())\r
4412             {\r
4413 //                response = responseForSuccess(request);\r
4414 //                delaySuspend();\r
4415             }\r
4416             else\r
4417             {\r
4418 //                response = responseForFailed(request);\r
4419             }\r
4420         }\r
4421 \r
4422         //\r
4423         else if (uri->isMatch("^/exec_vlc.html$"))\r
4424         {\r
4425 //            _vlc->execute();\r
4426 //            return responseForRefreshMain(request, client);\r
4427         }\r
4428 \r
4429         //\r
4430         // tuner control\r
4431         //\r
4432         else if (uri->isMatch("^/[0-9]{3}/"))\r
4433         {\r
4434 DebugLog0("tuner control");\r
4435             // String::substringWithRange() を実装するのを後回しにするので。。\r
4436             std::string s = uri->cString();\r
4437             int tuner = atoi(s.substr(1, 3).c_str());\r
4438             if ((0 <= tuner) && (tuner < _tunerCount))\r
4439             {\r
4440                 response = requestTunerControl(request, client, tuner);\r
4441 DebugLog0("tuner control done");\r
4442             }\r
4443         }\r
4444 \r
4445         //\r
4446         // reservation control\r
4447         //\r
4448         else if (uri->isMatch("^/reservation.html$"))\r
4449         {\r
4450             response = responseForReservation(request, client);\r
4451         }\r
4452         else if (uri->isMatch("^/regist.cgi"))\r
4453         {\r
4454             response = responseForRegistCGI(request, client);\r
4455         }\r
4456         else if (uri->isMatch("^/cancel.cgi"))\r
4457         {\r
4458             response = responseForCancelCGI(request, client);\r
4459         }\r
4460         else if (uri->isMatch("^/add_keywords.cgi"))\r
4461         {\r
4462             response = responseForAddKeywordsCGI(request, client);\r
4463         }\r
4464         else if (uri->isMatch("^/mod_keywords.cgi"))\r
4465         {\r
4466             response = responseForModKeywordsCGI(request, client);\r
4467         }\r
4468         else if (uri->isMatch("^/reservation.xml$"))\r
4469         {\r
4470         }\r
4471         else if (uri->isMatch("^/[0-9]{6}/"))\r
4472         {\r
4473 //            DebugLog2("reservation: %s\n", uri.c_str());\r
4474         }\r
4475 \r
4476         //\r
4477         // video control\r
4478         //\r
4479         else if (uri->isMatch("^/[0-9]{9}/"))\r
4480         {\r
4481 //            DebugLog2("video: %s\n", uri.c_str());\r
4482         }\r
4483 \r
4484         //\r
4485         // program\r
4486         //\r
4487 //        else if (match(uri.c_str(), "^/programs.html$"))\r
4488         else if (uri->isMatch("^/" URI_PROGRAMS_HTML "$"))\r
4489         {\r
4490             response = responseForPrograms(request, client);\r
4491         }\r
4492         else if (uri->isMatch("^/programs.xml$"))\r
4493         {\r
4494 //            collectEPG();\r
4495 //            return responseWithDictionary(request, _programs);\r
4496         }\r
4497 \r
4498         //\r
4499         //\r
4500         else if (uri->isMatch("^/tv.html$"))\r
4501         {\r
4502 //            return responseForTV(request, client);\r
4503         }\r
4504 \r
4505         // IPTV対応\r
4506         else if (uri->isMatch("^/iptv.m3u8$"))\r
4507         {\r
4508             return responseForPlaylist(request, client);\r
4509         }\r
4510 \r
4511         //\r
4512         else if (uri->isMatch("^/iptd.log$"))\r
4513         {\r
4514             String *path = _system_path->stringByAppendingPathComponent("log");\r
4515             if (path != NULL)\r
4516             {\r
4517                 path = path->stringByAppendingPathComponent("iptd.log");\r
4518                 if (path != NULL)\r
4519                 {\r
4520                     response = _httpd->responseWithPath(path, request);\r
4521                     if (response != NULL)\r
4522                     {\r
4523                         if (response->message() != NULL)\r
4524                         {\r
4525                             if (response->message()->header() != NULL)\r
4526                             {\r
4527                                 response->message()->header()->setFieldBodyWithName("text/plane; charset=UTF-8", "Content-Type");\r
4528                             }\r
4529                         }\r
4530                     }\r
4531                 }\r
4532             }\r
4533         }\r
4534     }\r
4535     else if (request->method()->isEqualToString("POST"))\r
4536     {\r
4537         // URI\r
4538         String *uri = request->URI();\r
4539         DebugLog1("POST: %s\n", uri->cString());\r
4540         InternetTextMessage *message = request->message();\r
4541         if (message != NULL)\r
4542         {\r
4543             DebugLog3("message: ");\r
4544             InternetTextMessageHeader *header = message->header();\r
4545             if (header != NULL)\r
4546             {\r
4547                 DebugLog3("header: ");\r
4548             }\r
4549             InternetTextMessageBody *body = message->body();\r
4550             if (body != NULL)\r
4551             {\r
4552                 DebugLog3("body: ");\r
4553             }\r
4554         }\r
4555     }\r
4556 \r
4557     return response;\r
4558 }\r
4559 \r
4560 #ifndef _WIN32\r
4561 #pragma mark '\r
4562 #pragma mark ------- プロパティ取得 -------\r
4563 #endif\r
4564 \r
4565 bool Controller::isTunerInitialized(int tuner)\r
4566 {\r
4567     DebugLog2("Controller::isTunerInitialized()");\r
4568 \r
4569     bool result = false;\r
4570 \r
4571     if ((0 <= tuner) && (tuner < _tunerCount))\r
4572     {\r
4573         // lock\r
4574         RaymLock(this);\r
4575 \r
4576         Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
4577         if (tunersInfo != NULL)\r
4578         {\r
4579             Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
4580             if (tunerInfo != NULL)\r
4581             {\r
4582                 result = tunerInfo->boolForKey(KEY_INITIALIZED);\r
4583             }\r
4584         }\r
4585 \r
4586         // unlock\r
4587         RaymUnlock(this);\r
4588     }\r
4589 \r
4590     return result;\r
4591 }\r
4592 \r
4593 bool Controller::isTunerEnabled(int tuner)\r
4594 {\r
4595     DebugLog2("Controller::isTunerEnabled()");\r
4596 \r
4597     bool result = false;\r
4598 \r
4599     if ((0 <= tuner) && (tuner < _tunerCount))\r
4600     {\r
4601         // lock\r
4602         RaymLock(this);\r
4603 \r
4604         Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
4605         if (tunersInfo != NULL)\r
4606         {\r
4607             Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
4608             if (tunerInfo != NULL)\r
4609             {\r
4610                 result = tunerInfo->boolForKey(KEY_ENABLED);\r
4611             }\r
4612         }\r
4613 \r
4614         // unlock\r
4615         RaymUnlock(this);\r
4616     }\r
4617 \r
4618     return result;\r
4619 }\r
4620 \r
4621 bool Controller::isChannelEnabled(int tuner, int channel)\r
4622 {\r
4623     DebugLog2("Controller::isChannelEnabled()");\r
4624 \r
4625     bool result = false;\r
4626 \r
4627     if ((0 <= tuner) && (tuner < _tunerCount) && (0 <= channel) && (channel <= Tuner::MAX_CHANNELS_ISDB_T))\r
4628     {\r
4629         // lock\r
4630         RaymLock(this);\r
4631 \r
4632         Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
4633         if (tunersInfo != NULL)\r
4634         {\r
4635             Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
4636             if (tunerInfo != NULL)\r
4637             {\r
4638                 Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
4639                 if (channels != NULL)\r
4640                 {\r
4641                     char key[4];\r
4642                     sprintf_s(key, "%03d", channel);\r
4643                     Dictionary *chInfo = channels->dictionaryForKey(key);\r
4644                     if (chInfo != NULL)\r
4645                     {\r
4646                         result = chInfo->boolForKey(KEY_ENABLED);\r
4647                     }\r
4648                 }\r
4649             }\r
4650         }\r
4651 \r
4652         // unlock\r
4653         RaymUnlock(this);\r
4654     }\r
4655 \r
4656     return result;\r
4657 }\r
4658 \r
4659 Array *Controller::stationInfos(Tuner::Type type)\r
4660 {\r
4661     DebugLog2("Controller::stationInfosForISDB_T()");\r
4662 \r
4663     Array *result = Array::arrayWithCapacity(0);\r
4664 \r
4665     // lock\r
4666     RaymLock(this);\r
4667 \r
4668     Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
4669     if (tunersInfo != NULL)\r
4670     {\r
4671         for (int i = 0; i < _tunerCount; ++i)\r
4672         {\r
4673             if (_tuners[i]->type() == type)\r
4674             {\r
4675                 Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[i]->name());\r
4676                 if (tunerInfo != NULL)\r
4677                 {\r
4678                     Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
4679                     if (channels != NULL)\r
4680                     {\r
4681                         for (uint ch = 0; ch <= (type == Tuner::ISDB_T ? Tuner::MAX_CHANNELS_ISDB_T : Tuner::MAX_CHANNELS_ISDB_S); ++ch)\r
4682                         {\r
4683                             char chkey[4];\r
4684                             sprintf_s(chkey, "%03d", ch);\r
4685                             Dictionary *channel = channels->dictionaryForKey(chkey);\r
4686                             if ((channel != NULL) && channel->boolForKey(KEY_ENABLED))\r
4687                             {\r
4688                                 result->addObject(channel);\r
4689                             }\r
4690                         }\r
4691                     }\r
4692                 }\r
4693                 break;\r
4694             }\r
4695         }\r
4696     }\r
4697 \r
4698     // unlock\r
4699     RaymUnlock(this);\r
4700 \r
4701     return result;\r
4702 }\r
4703 \r
4704 String *Controller::stationName(Tuner::Type type, int channel)\r
4705 {\r
4706     DebugLog2("Controller::stationName()");\r
4707 \r
4708     String *result = NULL;\r
4709 \r
4710     // lock\r
4711     RaymLock(this);\r
4712 \r
4713     Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
4714     if (tunersInfo != NULL)\r
4715     {\r
4716         for (int i = 0; i < _tunerCount; ++i)\r
4717         {\r
4718             if (_tuners[i]->type() == type)\r
4719             {\r
4720                 Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[i]->name());\r
4721                 if (tunerInfo != NULL)\r
4722                 {\r
4723                     Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
4724                     if (channels != NULL)\r
4725                     {\r
4726                         char chkey[4];\r
4727                         sprintf_s(chkey, "%03d", channel);\r
4728                         Dictionary *channel = channels->dictionaryForKey(chkey);\r
4729                         if ((channel != NULL) && channel->boolForKey(KEY_ENABLED))\r
4730                         {\r
4731                             result = channel->stringForKey(KEY_NAME);\r
4732                         }\r
4733                     }\r
4734                 }\r
4735                 break;\r
4736             }\r
4737         }\r
4738     }\r
4739 \r
4740     // unlock\r
4741     RaymUnlock(this);\r
4742 \r
4743     return result;\r
4744 }\r
4745 \r
4746 // epgの開始時刻でソートする為の比較関数\r
4747 Integer compareFunction(Object *obj1, Object *obj2, void *context)\r
4748 {\r
4749     if (isKindOfClass(Dictionary, obj1) && isKindOfClass(Dictionary, obj2))\r
4750     {\r
4751         time_t st1;\r
4752         time_t ed1;\r
4753         Controller::getTimeWithEPG((Dictionary *)obj1, &st1, &ed1);\r
4754 \r
4755         time_t st2;\r
4756         time_t ed2;\r
4757         Controller::getTimeWithEPG((Dictionary *)obj2, &st2, &ed2);\r
4758 \r
4759         if (st1 < st2)\r
4760         {\r
4761             return OrderedAscending;\r
4762         }\r
4763         else if (st1 > st2)\r
4764         {\r
4765             return OrderedDescending;\r
4766         }\r
4767         else\r
4768         {\r
4769             if (ed1 < ed2)\r
4770             {\r
4771                 return OrderedAscending;\r
4772             }\r
4773             else if (ed1 > ed2)\r
4774             {\r
4775                 return OrderedDescending;\r
4776             }\r
4777         }\r
4778     }\r
4779     return OrderedSame;\r
4780 }\r
4781 \r
4782 Array *Controller::programsForServices(Array *services)\r
4783 {\r
4784     DebugLog2("Controller::programsForServices()");\r
4785 \r
4786     Array *result = Array::arrayWithCapacity(0);\r
4787 \r
4788     // lock\r
4789     RaymLock(this);\r
4790 \r
4791     for (uint i = 0; i < services->count(); ++i)\r
4792     {\r
4793         Dictionary *service = (Dictionary *)services->objectAtIndex(i);\r
4794         Dictionary *events = _epgs->dictionaryForKey(service->stringForKey(KEY_SERVICE_ID));\r
4795         if (events != NULL)\r
4796         {\r
4797             Array *keys = events->allKeys();\r
4798             for (uint j = 0; j < keys->count(); ++j)\r
4799             {\r
4800                 result->addObject(events->objectForKey((String *)keys->objectAtIndex(j)));\r
4801             }\r
4802         }\r
4803     }\r
4804 \r
4805     // sort\r
4806     result = result->sortedArrayUsingFunction(compareFunction, this);\r
4807 \r
4808 \r
4809     // unlock\r
4810     RaymUnlock(this);\r
4811 \r
4812     return result;\r
4813 }\r
4814 \r
4815 String *Controller::stationNameForServiceID(String *service_id)\r
4816 {\r
4817     DebugLog2("Controller::stationNameForServiceID()");\r
4818 \r
4819     String *result = NULL;\r
4820 \r
4821     if (service_id != NULL)\r
4822     {\r
4823         // lock\r
4824         RaymLock(this);\r
4825 \r
4826         Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
4827         if (tunersInfo != NULL)\r
4828         {\r
4829             Array *tuners_key = tunersInfo->allKeys();\r
4830 \r
4831             for (uint i = 0; (result == NULL) && (i < tuners_key->count()); ++i)\r
4832             {\r
4833                 Dictionary *tunerInfo = tunersInfo->dictionaryForKey((String *)tuners_key->objectAtIndex(i));\r
4834                 if (tunerInfo != NULL)\r
4835                 {\r
4836                     Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
4837                     if (channels != NULL)\r
4838                     {\r
4839                         Array *chkey = channels->allKeys();\r
4840                         for (uint ch = 0; (result == NULL) && (ch < chkey->count()); ++ch)\r
4841                         {\r
4842                             Dictionary *channel = channels->dictionaryForKey((String *)chkey->objectAtIndex(ch));\r
4843                             if (channel != NULL)\r
4844                             {\r
4845                                 Array *services = channel->arrayForKey(KEY_SERVICES);\r
4846                                 if (services != NULL)\r
4847                                 {\r
4848                                     for (uint j = 0; j < services->count(); ++j)\r
4849                                     {\r
4850                                         Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
4851                                         String *sid = service->stringForKey(KEY_SERVICE_ID);\r
4852                                         if (sid != NULL)\r
4853                                         {\r
4854                                             if (sid->isEqualToString(service_id))\r
4855                                             {\r
4856                                                 result = service->stringForKey(KEY_NAME);\r
4857                                                 break;\r
4858                                             }\r
4859                                         }\r
4860                                     }\r
4861                                 }\r
4862                             }\r
4863                         }\r
4864                     }\r
4865                 }\r
4866             }\r
4867         }\r
4868 \r
4869         // unlock\r
4870         RaymUnlock(this);\r
4871     }\r
4872 \r
4873     return result;\r
4874 }\r
4875 \r
4876 #ifndef _WIN32\r
4877 #pragma mark '\r
4878 #pragma mark ------- タイマディスパッチャ -------\r
4879 #endif\r
4880 \r
4881 void Controller::timerExpired(Timer *timer, void *userInfo)\r
4882 {\r
4883     DebugLog2("Controller::timerExpired()");\r
4884 \r
4885     //\r
4886 \r
4887     switch ((long long)userInfo)\r
4888     {\r
4889     case CMD_RESTART:\r
4890         if (restart() > 0)\r
4891         {\r
4892             // 初期化成功\r
4893             DebugLog2("tuner initialize success.");\r
4894 \r
4895             // EPG収集用タイマ起動(ISDB-S)\r
4896             _timer_epg_s = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);\r
4897             if (_timer_epg_s != NULL)\r
4898             {\r
4899                 _timer_epg_s->fire();\r
4900             }\r
4901 \r
4902             // EPG収集用タイマ起動(ISDB-T)\r
4903             _timer_epg_t = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);\r
4904             if (_timer_epg_t != NULL)\r
4905             {\r
4906                 _timer_epg_t->fire();\r
4907             }\r
4908         }\r
4909         else\r
4910         {\r
4911             // 失敗\r
4912             DebugLog0("tuner initialize failed.");\r
4913         }\r
4914         break;\r
4915 \r
4916     case CMD_SUSPEND:\r
4917         if (canSuspend())\r
4918         {\r
4919             suspend();\r
4920         }\r
4921         break;\r
4922 \r
4923     case CMD_PERIODIC:                                  // 周期処理\r
4924         periodic();\r
4925         break;\r
4926 \r
4927     case CMD_PERIODIC_2:                                // 周期処理2\r
4928         periodic_2();\r
4929         break;\r
4930 \r
4931     case CMD_COLLECT_EPG_ISDB_S:                        // EPG収集(ISDB-S)\r
4932         // 番組情報収集(ISDB-S)\r
4933         if (collectEPGs(Tuner::ISDB_S))\r
4934         {\r
4935             // 終了(繰り返し無し)\r
4936             _timer_epg_s->setRepeats(false);\r
4937         }\r
4938         else\r
4939         {\r
4940             // リトライ\r
4941             _timer_epg_s->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
4942         }\r
4943         break;\r
4944 \r
4945     case CMD_COLLECT_EPG_ISDB_T:                        // 番組情報収集(ISDB-T)\r
4946         if (collectEPGs(Tuner::ISDB_T))\r
4947         {\r
4948             // 終了(繰り返し無し)\r
4949             _timer_epg_t->setRepeats(false);\r
4950         }\r
4951         else\r
4952         {\r
4953             // リトライ\r
4954             _timer_epg_t->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
4955         }\r
4956         break;\r
4957     }\r
4958 }\r
4959 \r
4960 #ifndef _WIN32\r
4961 #pragma mark '\r
4962 #pragma mark ------- 起動/停止関連 -------\r
4963 #endif\r
4964 \r
4965 // システム状態をチェックしてサスペンド可能かを返す\r
4966 bool Controller::canSuspend()\r
4967 {\r
4968     return isIdleState(true);\r
4969 }\r
4970 \r
4971 bool Controller::canTerminate()\r
4972 {\r
4973     return isIdleState(false);\r
4974 }\r
4975 \r
4976 bool Controller::isIdleState(bool suspend)\r
4977 {\r
4978     DebugLog2("Controller::isIdleState() start.");\r
4979 \r
4980     bool result = false;\r
4981 \r
4982     // lock\r
4983     RaymLock(this);\r
4984 \r
4985     // 初期化済みか\r
4986     while (_initialized)\r
4987     {\r
4988         // EPG(ISDB-S)収集中か\r
4989         if (suspend && (_timer_epg_s != NULL) && _timer_epg_s->valid())\r
4990         {\r
4991             // サスペンド不可\r
4992             break;\r
4993         }\r
4994 \r
4995         // EPG(ISDB-T)収集中か\r
4996         if (suspend && (_timer_epg_t != NULL) && _timer_epg_t->valid())\r
4997         {\r
4998             // サスペンド不可\r
4999             break;\r
5000         }\r
5001 \r
5002         // サスペンド可に設定\r
5003         result = true;\r
5004 \r
5005         // 予約情報をチェック\r
5006         // 10分後以内に開始の予約があったらサスペンド不可\r
5007 \r
5008         // 現在時刻取得\r
5009         time_t now = time(NULL);\r
5010 \r
5011         for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
5012         {\r
5013             Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
5014             if ((array == NULL) || (array->count() == 0))\r
5015             {\r
5016                 // next tuner\r
5017                 continue;\r
5018             }\r
5019 \r
5020             //\r
5021             Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
5022             time_t start;\r
5023             time_t end;\r
5024             getTimeWithEPG(epg, &start, &end);\r
5025 \r
5026             if (start + OFFSET_OF_SUPPRESSION_TIME <= now)\r
5027             {\r
5028                 // サスペンド不可\r
5029                 result = false;\r
5030                 break;\r
5031             }\r
5032         }\r
5033 \r
5034         break;\r
5035     }\r
5036 \r
5037     // unlock\r
5038     RaymUnlock(this);\r
5039 \r
5040     DebugLog2("Controller::isIdleState() end.");\r
5041 \r
5042     return result;\r
5043 }\r
5044 \r
5045 /*\r
5046  * TrayApp::WndProc() からコールされる\r
5047  *\r
5048  *   スレッドコンテキスト:メインスレッド\r
5049  */\r
5050 void Controller::systemWillSuspend()\r
5051 {\r
5052     DebugLog2("Controller::systemWillSuspend() start");\r
5053 \r
5054     RaymLock(this);\r
5055     _cancel_epg_collect = true;\r
5056     RaymUnlock(this);\r
5057 \r
5058     // タイマ停止\r
5059     if ((_timer_restart != NULL) && _timer_restart->valid())\r
5060     {\r
5061         _timer_restart->invalidate();\r
5062     }\r
5063     if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
5064     {\r
5065         _timer_epg_s->invalidate();\r
5066     }\r
5067     if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
5068     {\r
5069         _timer_epg_t->invalidate();\r
5070     }\r
5071     if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
5072     {\r
5073         _timer_periodic->invalidate();\r
5074     }\r
5075     if ((_timer_periodic_2 != NULL) && _timer_periodic_2->valid())\r
5076     {\r
5077         _timer_periodic_2->invalidate();\r
5078     }\r
5079 \r
5080     // lock\r
5081     RaymLock(this);\r
5082 \r
5083     // タイマ解放\r
5084     RELEASE(_timer_restart);\r
5085     RELEASE(_timer_epg_s);\r
5086     RELEASE(_timer_epg_t);\r
5087     RELEASE(_timer_periodic);\r
5088     RELEASE(_timer_periodic_2);\r
5089 \r
5090     // スケジュール更新\r
5091     updateSchedule();\r
5092 \r
5093     // 未初期化に設定\r
5094     _initialized = false;\r
5095 \r
5096     // チューナ解放\r
5097     for (int i = 0; i < _tunerCount; ++i)\r
5098     {\r
5099         if (_tuners[i] != NULL)\r
5100         {\r
5101             delete _tuners[i];\r
5102             _tuners[i] = NULL;\r
5103         }\r
5104     }\r
5105 \r
5106     _cancel_epg_collect = false;\r
5107 \r
5108     DebugLog0("system will suspend...");\r
5109 \r
5110     // unlock\r
5111     RaymUnlock(this);\r
5112 \r
5113 #ifdef OBJC_MEMORY_CHECK\r
5114     DebugLog0("global_objc_count_ = %d", Raym::global_objc_count_);\r
5115 #endif\r
5116 \r
5117     DebugLog2("Controller::systemWillSuspend() end");\r
5118 }\r
5119 \r
5120 /*\r
5121  * TrayApp::WndProc() からコールされる\r
5122  *\r
5123  *   スレッドコンテキスト:メインスレッド\r
5124  */\r
5125 void Controller::systemResumed()\r
5126 {\r
5127     DebugLog2("Controller::systemResumed() start");\r
5128 \r
5129     DebugLog0("system resumed.");\r
5130 \r
5131     if (_timer_restart == NULL)\r
5132     {\r
5133         // 再開タイマ起動\r
5134         _timer_restart = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_RESTART, false);\r
5135         _timer_restart->fire();\r
5136     }\r
5137 \r
5138     DebugLog2("Controller::systemResumed()");\r
5139 }\r
5140 \r
5141 /*\r
5142  * TrayApp::WndProc() からコールされる\r
5143  *\r
5144  *   スレッドコンテキスト:メインスレッド\r
5145  */\r
5146 void Controller::detectIdle()\r
5147 {\r
5148     DebugLog2("Controller::detectIdle()");\r
5149 \r
5150     // ここはメインスレッドなのでARPを用意する\r
5151     AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
5152 \r
5153     // lock\r
5154     RaymLock(this);\r
5155 \r
5156     // サスペンド可能か確認\r
5157     if (canSuspend())\r
5158     {\r
5159         if (_idle_count == 0)\r
5160         {\r
5161             DebugLog0("detect idle...");\r
5162         }\r
5163 \r
5164         // アイドルカウンタを更新\r
5165         ++_idle_count;\r
5166 \r
5167         // 起動中アプリと休止状態抑止アプリのチェック\r
5168         bool found = false;\r
5169 \r
5170         Array *dont_in_suspend = _props->arrayForKey(KEY_DO_NOT_IN_SUSPEND);\r
5171         Array *running_apps = Workspace::sharedWorkspace()->runningApplications();\r
5172         for (uint i = 0; (i < running_apps->count()) && !found; ++i)\r
5173         {\r
5174             RunningApplication *ra = (RunningApplication *)running_apps->objectAtIndex(i);\r
5175 \r
5176             // 実行中でなければ次へ\r
5177             if (!ra->isRunning())\r
5178             {\r
5179                 continue;\r
5180             }\r
5181 \r
5182             // 実行ファイルのチェック\r
5183             String *path = ra->executePath();\r
5184             if ((path == NULL) || (path->length() == 0))\r
5185             {\r
5186                 continue;\r
5187             }\r
5188             DebugLog3("exec path: %s", path->cString());\r
5189 \r
5190             // 休止状態抑止アプリリストのチェック\r
5191             for (uint j = 0; (j < dont_in_suspend->count()) && !found; ++j)\r
5192             {\r
5193                 found = path->isMatch((String *)dont_in_suspend->objectAtIndex(j));\r
5194             }\r
5195         }\r
5196 \r
5197         // 抑止有効なら KEY_FORCED_SUSPEND_TIME、無効なら KEY_SUSPEND_TIME で取得\r
5198         int limit = _props->integerForKey(found ? KEY_FORCED_SUSPEND_TIME : KEY_SUSPEND_TIME);\r
5199         DebugLog3("found: %d, _idle_count: %d, limit: %d", found, _idle_count, limit);\r
5200         if (_idle_count >= limit)\r
5201         {\r
5202             // unlock\r
5203             RaymUnlock(this);\r
5204 \r
5205             // サスペンド前にARPを解放しておく\r
5206             pool->release();\r
5207 \r
5208             // 下記の TrayApp::suspend() を使用する場合、自アプリへsuspendメッセージが\r
5209             // ブロードキャストされないため、自分でコールしておく\r
5210             systemWillSuspend();\r
5211 \r
5212             // サスペンド\r
5213             suspend();\r
5214 \r
5215             // 再度 ARPを用意\r
5216             pool = AutoreleasePool::alloc()->init();\r
5217 \r
5218             // lock\r
5219             RaymLock(this);\r
5220 \r
5221             // アイドルカウンタをクリア\r
5222             _idle_count = 0;\r
5223         }\r
5224     }\r
5225     else\r
5226     {\r
5227         // アイドルカウンタをクリア\r
5228         _idle_count = 0;\r
5229     }\r
5230 \r
5231     // unlock\r
5232     RaymUnlock(this);\r
5233 \r
5234     // ARP解放\r
5235     pool->release();\r
5236 }\r
5237 \r
5238 void Controller::detectNonIdle()\r
5239 {\r
5240     DebugLog2("Controller::detectNonIdle()");\r
5241 \r
5242     // lock\r
5243     RaymLock(this);\r
5244 \r
5245     if (_idle_count > 0)\r
5246     {\r
5247         DebugLog0("detect non idle...");\r
5248     }\r
5249     _idle_count = 0;\r
5250 \r
5251     // unlock\r
5252     RaymUnlock(this);\r
5253 }\r
5254 \r
5255 int Controller::restart()\r
5256 {\r
5257     // lock\r
5258     RaymLock(this);\r
5259 \r
5260     // 未初期化に設定\r
5261     _initialized = false;\r
5262 \r
5263     // チューナ解放\r
5264     for (int i = 0; i < _tunerCount; ++i)\r
5265     {\r
5266         if (_tuners[i] != NULL)\r
5267         {\r
5268             delete _tuners[i];\r
5269             _tuners[i] = NULL;\r
5270         }\r
5271     }\r
5272 \r
5273     // 古い予約情報の破棄\r
5274 \r
5275 \r
5276     // チューナ初期化\r
5277     _tunerCount = ry0::device::TunerFactory::scan(_tuners, _multi2_dll);\r
5278 \r
5279     // unlock\r
5280     RaymUnlock(this);\r
5281 \r
5282     // チューナでループ\r
5283     for (int i = 0; i < _tunerCount; ++i)\r
5284     {\r
5285         // チューナが初期化済みか\r
5286         if (!isTunerInitialized(i))\r
5287         {\r
5288             // チャンネルスキャン\r
5289             scanChannel(i);\r
5290         }\r
5291 \r
5292         if (isTunerEnabled(i))\r
5293         {\r
5294             // チャンネル設定\r
5295             setChannel(i, getChannel(i));\r
5296         }\r
5297     }\r
5298 \r
5299     // UDPポートマッピング\r
5300     DebugLog0("  UDP Port Mapping    :");\r
5301     DebugLog0("    tuner, ch -> port");\r
5302     int udpport = _props->integerForKey(KEY_BEGIN_UDP_PORT);\r
5303     for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
5304     {\r
5305         uint max_channel = (_tuners[tuner]->type() == Tuner::ISDB_S ? Tuner::MAX_CHANNELS_ISDB_S : Tuner::MAX_CHANNELS_ISDB_T);\r
5306         for (uint ch = 0; ch <= max_channel; ++ch)\r
5307         {\r
5308             if (isChannelEnabled(tuner, ch))\r
5309             {\r
5310                 DebugLog0("    %5d,%3d -> %5d", tuner, ch, udpport);\r
5311 \r
5312                 Dictionary *udp_to_tuner_channel = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
5313                 if (udp_to_tuner_channel == NULL)\r
5314                 {\r
5315                     udp_to_tuner_channel = Dictionary::dictionaryWithCapacity(0);\r
5316                     _streaming_ctrls->setObject(udp_to_tuner_channel, KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
5317                 }\r
5318 \r
5319                 Dictionary *tuner_channel_to_udp = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
5320                 if (tuner_channel_to_udp == NULL)\r
5321                 {\r
5322                     tuner_channel_to_udp = Dictionary::dictionaryWithCapacity(0);\r
5323                     _streaming_ctrls->setObject(tuner_channel_to_udp, KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
5324                 }\r
5325 \r
5326                 char port_str[10];\r
5327                 sprintf_s(port_str, "%d", udpport);\r
5328                 char tuner_and_channel[10];\r
5329                 sprintf_s(tuner_and_channel, "%d,%d", tuner, ch);\r
5330 \r
5331                 udp_to_tuner_channel->setString(tuner_and_channel, port_str);\r
5332                 tuner_channel_to_udp->setString(port_str, tuner_and_channel);\r
5333 \r
5334                 ++udpport;\r
5335             }\r
5336         }\r
5337     }\r
5338 \r
5339     // 周期タイマ起動\r
5340     _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC, true);\r
5341     _timer_periodic->fire();\r
5342 \r
5343     // 周期タイマ2起動\r
5344     _timer_periodic_2 = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC_2, true);\r
5345     _timer_periodic_2->fire();\r
5346 \r
5347     // lock\r
5348     RaymLock(this);\r
5349 \r
5350     // 初期化済みに設定\r
5351     _initialized = true;\r
5352 \r
5353     // unlock\r
5354     RaymUnlock(this);\r
5355 \r
5356     return (_tunerCount > 0);\r
5357 }\r
5358 \r
5359 int Controller::start()\r
5360 {\r
5361     // ログファイル数設定\r
5362     //   設定以前にログ出力しないこと\r
5363     Raym::LOG_NUM_MAX = 8;\r
5364 \r
5365 #ifdef RAYM_MEMORY_CHECK\r
5366     DebugLog0("");\r
5367     DebugLog0("global_raym_count_ = %d", Raym::global_raym_count_);\r
5368 #endif\r
5369 \r
5370     //\r
5371     AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
5372 \r
5373     // メンバ初期化\r
5374     _system_path        = NULL;\r
5375     _props_path         = NULL;\r
5376     _props              = NULL;\r
5377     _status_path        = NULL;\r
5378     _status             = NULL;\r
5379     _epgs_path          = NULL;\r
5380     _epgs               = NULL;\r
5381     _store_path         = NULL;\r
5382     _reservations       = NULL;\r
5383     _reservations_path  = NULL;\r
5384     _timer_restart      = NULL;\r
5385     _timer_periodic     = NULL;\r
5386     _timer_periodic_2   = NULL;\r
5387     _timer_epg_s        = NULL;\r
5388     _timer_epg_t        = NULL;\r
5389     _multi2_dll         = NULL;\r
5390     _streaming_ctrls    = NULL;\r
5391 \r
5392     _idle_count         = 0;\r
5393     _initialized        = false;\r
5394     _reservation_seq_id = 0;\r
5395     _cancel_epg_collect = false;\r
5396 \r
5397     _tunerCount = 0;\r
5398     for (int i = 0; i < ry0::device::MAX_TUNERS; ++i)\r
5399     {\r
5400         _tuners[i] = NULL;\r
5401     }\r
5402 \r
5403     DebugLog0("");\r
5404     DebugLog0("------------------------------------------------------------------------");\r
5405     DebugLog0("iPTd ver %s (rev.%d)", VERSION, REVISION);\r
5406     DebugLog0("initialize...");\r
5407 \r
5408     int result = 0;\r
5409 \r
5410     // 初期化\r
5411     while (true)\r
5412     {\r
5413         bool updated = false;\r
5414 \r
5415         // システムパス設定\r
5416         _system_path = String::stringWithFormat("%s", GetExecutePath());\r
5417         if (_system_path == NULL)\r
5418         {\r
5419             DebugLog0("error: GetExecutePath()");\r
5420             result = -1;\r
5421             break;\r
5422         }\r
5423         _system_path = _system_path->stringByReplacingOccurrencesOfString("iPTd.exe", "");\r
5424         _system_path->retain();\r
5425         DebugLog2("_system_path: %s", _system_path->cString());\r
5426 \r
5427         // プロパティファイルのパス設定\r
5428         _props_path = String::alloc()->initWithFormat("%s%s.iptd.plist", _system_path->cString(), PLIST_PREFIX);\r
5429         if (_props_path == NULL)\r
5430         {\r
5431             DebugLog0("error: set property file path.");\r
5432             result = -1;\r
5433             break;\r
5434         }\r
5435 \r
5436         // プロパティの読み込み\r
5437         _props = Dictionary::alloc()->initWithContentsOfFile(_props_path);\r
5438         if (_props == NULL)\r
5439         {\r
5440             DebugLog1("property file: %s (created)", _props_path->cString());\r
5441             _props = Dictionary::alloc()->initWithCapacity(0);\r
5442         }\r
5443         else\r
5444         {\r
5445             DebugLog1("property file: %s", _props_path->cString());\r
5446         }\r
5447 \r
5448         // ステータスファイルのパス設定\r
5449         _status_path = String::alloc()->initWithFormat("%s%s.iptd.status.plist", _system_path->cString(), PLIST_PREFIX);\r
5450         if (_status_path == NULL)\r
5451         {\r
5452             DebugLog0("error: set status file path.");\r
5453             result = -1;\r
5454             break;\r
5455         }\r
5456 \r
5457         // ステータスの読み込み\r
5458         _status = Dictionary::alloc()->initWithContentsOfFile(_status_path);\r
5459         if (_status == NULL)\r
5460         {\r
5461             DebugLog1("status file: %s (created)", _status_path->cString());\r
5462             _status = Dictionary::alloc()->initWithCapacity(0);\r
5463         }\r
5464         else\r
5465         {\r
5466             DebugLog1("status file: %s", _status_path->cString());\r
5467         }\r
5468 \r
5469         // システム名\r
5470         if (_props->stringForKey(KEY_NAME) == NULL)\r
5471         {\r
5472             _props->setString(DEF_NAME, KEY_NAME);\r
5473             updated = true;\r
5474         }\r
5475 \r
5476         // ホスト名\r
5477         if (_props->stringForKey(KEY_HOSTNAME) == NULL)\r
5478         {\r
5479             _props->setString(DEF_HOSTNAME, KEY_HOSTNAME);\r
5480             updated = true;\r
5481         }\r
5482 \r
5483         // multi2デコード可否判定: multi2.dll が使えるか確認する\r
5484         _multi2_dll = ::LoadLibrary(L"multi2.dll");\r
5485         while (_multi2_dll != NULL)\r
5486         {\r
5487             // b25: 関数アドレス取得\r
5488             ARIB_STD_B25 *(*func_b25)();\r
5489             func_b25 = reinterpret_cast<ARIB_STD_B25 *(*)()>(::GetProcAddress(_multi2_dll, "create_arib_std_b25"));\r
5490             if (func_b25 == NULL)\r
5491             {\r
5492                 // 関数アドレス取得NG\r
5493                 FreeLibrary(_multi2_dll);\r
5494                 _multi2_dll = NULL;\r
5495                 break;\r
5496             }\r
5497 \r
5498             // b25: インスタンス生成\r
5499             ARIB_STD_B25 * _b25;\r
5500             _b25 = func_b25();\r
5501             if (_b25 == NULL)\r
5502             {\r
5503                 // インスタンス生成NG\r
5504                 FreeLibrary(_multi2_dll);\r
5505                 _multi2_dll = NULL;\r
5506                 break;\r
5507             }\r
5508             _b25->release(_b25);\r
5509 \r
5510             // bcas: 関数アドレス取得\r
5511             B_CAS_CARD *(*func_bcas)();\r
5512             func_bcas = reinterpret_cast<B_CAS_CARD *(*)()>(::GetProcAddress(_multi2_dll, "create_b_cas_card"));\r
5513             if (func_bcas == NULL)\r
5514             {\r
5515                 // 関数アドレス取得NG\r
5516                 FreeLibrary(_multi2_dll);\r
5517                 _multi2_dll = NULL;\r
5518                 break;\r
5519             }\r
5520 \r
5521             // bcas: インスタンス生成\r
5522             B_CAS_CARD * _bcas;\r
5523             _bcas = func_bcas();\r
5524             if (_bcas == NULL)\r
5525             {\r
5526                 // インスタンス生成NG\r
5527                 FreeLibrary(_multi2_dll);\r
5528                 _multi2_dll = NULL;\r
5529                 break;\r
5530             }\r
5531             if (_bcas->init(_bcas) != 0)\r
5532             {\r
5533                 // bcas初期化NG\r
5534                 FreeLibrary(_multi2_dll);\r
5535                 _multi2_dll = NULL;\r
5536                 break;\r
5537             }\r
5538             _bcas->release(_bcas);\r
5539 \r
5540             // デコード可\r
5541             break;\r
5542         }\r
5543 \r
5544         // HTTPd ポート\r
5545         if (_props->integerForKey(KEY_HTTP_PORT) == 0)\r
5546         {\r
5547             _props->setInteger(DEF_HTTP_PORT, KEY_HTTP_PORT);\r
5548             updated = true;\r
5549         }\r
5550 \r
5551         // 休止までの時間\r
5552         if ((_props->integerForKey(KEY_SUSPEND_TIME) < DEF_SUSPEND_TIME) ||\r
5553             (_props->integerForKey(KEY_SUSPEND_TIME) >= _props->integerForKey(KEY_FORCED_SUSPEND_TIME)))\r
5554         {\r
5555             _props->setInteger(DEF_SUSPEND_TIME, KEY_SUSPEND_TIME);\r
5556             updated = true;\r
5557         }\r
5558 \r
5559         // 強制休止までの時間\r
5560         if ((_props->integerForKey(KEY_FORCED_SUSPEND_TIME) == 0) ||\r
5561             (_props->integerForKey(KEY_FORCED_SUSPEND_TIME) <= _props->integerForKey(KEY_SUSPEND_TIME)))\r
5562         {\r
5563             _props->setInteger(DEF_FORCED_SUSPEND_TIME, KEY_FORCED_SUSPEND_TIME);\r
5564             updated = true;\r
5565         }\r
5566 \r
5567         // UDPポートの開始番号\r
5568         if (_props->integerForKey(KEY_BEGIN_UDP_PORT) == 0)\r
5569         {\r
5570             _props->setInteger(DEF_BEGIN_UDP_PORT, KEY_BEGIN_UDP_PORT);\r
5571             updated = true;\r
5572         }\r
5573 \r
5574         // EPGを収集する時間\r
5575         if ((_props->stringForKey(KEY_COLLECT_EPG_TIME) == NULL) ||\r
5576             !_props->stringForKey(KEY_COLLECT_EPG_TIME)->isMatch(String::stringWithUTF8String("^\\d\\d:\\d\\d:\\d\\d$")))\r
5577         {\r
5578             _props->setString(DEF_COLLECT_EPG_TIME, KEY_COLLECT_EPG_TIME);\r
5579             updated = true;\r
5580         }\r
5581 \r
5582         // 休止状態抑止アプリリスト\r
5583         if (_props->arrayForKey(KEY_DO_NOT_IN_SUSPEND) == NULL)\r
5584         {\r
5585             Array *apps = Array::arrayWithCapacity(0);\r
5586             apps->addObject(String::stringWithUTF8String("PIXELACORPORATION.*DtvView\\.exe$"));\r
5587             apps->addObject(String::stringWithUTF8String("Common7\\\\IDE\\\\devenv\\.exe$"));\r
5588             apps->addObject(String::stringWithUTF8String("Kodi\\\\Kodi\\.exe$"));\r
5589             _props->setObject(apps, KEY_DO_NOT_IN_SUSPEND);\r
5590             updated = true;\r
5591         }\r
5592 \r
5593         // 録画データ格納先の確認\r
5594         _store_path = _props->stringForKey(KEY_STORE_PATH);\r
5595         if (_store_path ==  NULL)\r
5596         {\r
5597             // プロパティに未設定の場合\r
5598             //   <Public Directory>/Videos を設定\r
5599             const char *public_dir = GetPublicDirectory();\r
5600             if (public_dir == NULL)\r
5601             {\r
5602                 DebugLog0("error: GetPublicDirectory().");\r
5603                 result = -1;\r
5604                 break;\r
5605             }\r
5606             _store_path = String::alloc()->initWithFormat("%s\\Videos", public_dir);\r
5607             _props->setString(_store_path, KEY_STORE_PATH);\r
5608 \r
5609             // 更新フラグ\r
5610             updated = true;\r
5611         }\r
5612         else\r
5613         {\r
5614             _store_path->retain();\r
5615         }\r
5616 \r
5617         // 実際にディレクトリが存在しているか確認\r
5618         FileManager *fm = FileManager::defaultManager();\r
5619         bool isDir = false;\r
5620         if (!fm->fileExistsAtPath(_store_path, &isDir))\r
5621         {\r
5622             isDir = false;\r
5623         }\r
5624         if (!isDir)\r
5625         {\r
5626             DebugLog0("error: \"%s\" is not exists.", _store_path->cString());\r
5627             result = -1;\r
5628             break;\r
5629         }\r
5630 \r
5631         // HLS Preset\r
5632         if (_props->dictionaryForKey(KEY_PRESETS) == NULL)\r
5633         {\r
5634             Dictionary *presets = Dictionary::dictionaryWithCapacity(0);\r
5635             _props->setObject(presets, KEY_PRESETS);\r
5636 \r
5637 // とりあえずこのまま\r
5638 // プリセットは、あとで実装。。。            \r
5639             Array *preset;\r
5640 \r
5641             // "default"\r
5642             preset = STR_ARRAY("-vcodec",         "libx264",\r
5643                               "-b:v",            "768k",\r
5644                               "-s",              "640x360",\r
5645                               "-acodec",         "libfaac",\r
5646                               "-b:a",            "96k",\r
5647                               "-ar",             "44100",\r
5648                               "-flags",          "+loop-global_header",\r
5649                               "-map",            "0",\r
5650                               "-bsf",            "h264_mp4toannexb",\r
5651                               "-f",              "segment",\r
5652                               "-segment_format", "mpegts",\r
5653                               "-segment_time",   "10",\r
5654                               NULL);\r
5655             presets->setObject(preset, KEY_DEFAULT);\r
5656 \r
5657             updated = true;\r
5658         }\r
5659 \r
5660         // キャッシュ(HLSの一時ファイル格納先)パス\r
5661         if (_props->stringForKey(KEY_CACHE_PATH) == NULL)\r
5662         {\r
5663             _props->setString(_store_path, KEY_CACHE_PATH);\r
5664             updated = true;\r
5665         }\r
5666 \r
5667         // プロパティファイルを保存\r
5668         if (updated)\r
5669         {\r
5670             DebugLog0("props updated.");\r
5671             if (!_props->writeToFile(_props_path, false))\r
5672             {\r
5673                 DebugLog0("Can't write property file.");\r
5674                 result = -1;\r
5675                 break;\r
5676             }\r
5677         }\r
5678 \r
5679         // プロパティの確認\r
5680         DebugLog0("  Name                : %s", _props->stringForKey(KEY_NAME)->cString());\r
5681         DebugLog0("  Decode Enabled      : %s", (_multi2_dll != NULL) ? "true" : "false");\r
5682         DebugLog0("  HTTP Port           : %d", _props->integerForKey(KEY_HTTP_PORT));\r
5683         DebugLog0("  Suspend Time        : %d min", _props->integerForKey(KEY_SUSPEND_TIME));\r
5684         DebugLog0("  Forced Suspend Time : %d min", _props->integerForKey(KEY_FORCED_SUSPEND_TIME));\r
5685         Array *apps = _props->arrayForKey(KEY_DO_NOT_IN_SUSPEND);\r
5686         if (apps != NULL)\r
5687         {\r
5688             DebugLog1("  Do not in suspend   :");\r
5689             for (uint i = 0; i < apps->count(); ++i)\r
5690             {\r
5691                 DebugLog1("    RegExp[%02d]        : %s", i, ((String *)apps->objectAtIndex(i))->cString());\r
5692             }\r
5693         }\r
5694 \r
5695         // 番組データファイルのパス設定\r
5696         _epgs_path = String::alloc()->initWithFormat("%s%s.iptd.epgs.plist", _system_path->cString(), PLIST_PREFIX);\r
5697         if (_epgs_path == NULL)\r
5698         {\r
5699             DebugLog0("error: set epgs file path.");\r
5700             result = -1;\r
5701             break;\r
5702         }\r
5703 \r
5704         // 番組データの読み込み\r
5705         _epgs = Dictionary::alloc()->initWithContentsOfFile(_epgs_path);\r
5706         if (_epgs == NULL)\r
5707         {\r
5708             DebugLog1("epgs file: %s (created)", _epgs_path->cString());\r
5709             _epgs = Dictionary::alloc()->initWithCapacity(0);\r
5710         }\r
5711         else\r
5712         {\r
5713             DebugLog1("epgs file: %s", _epgs_path->cString());\r
5714 \r
5715             // 過去の番組データは削除\r
5716             removePastEPGs();\r
5717         }\r
5718 \r
5719         // 予約データファイルのパス設定\r
5720         _reservations_path = String::alloc()->initWithFormat("%s%s.iptd.reservations.plist", _system_path->cString(), PLIST_PREFIX);\r
5721         if (_reservations_path == NULL)\r
5722         {\r
5723             DebugLog0("error: set reservations file path.");\r
5724             result = -1;\r
5725             break;\r
5726         }\r
5727 \r
5728         // 予約データの読み込み\r
5729         _reservations = Dictionary::alloc()->initWithContentsOfFile(_reservations_path);\r
5730         if (_reservations == NULL)\r
5731         {\r
5732             DebugLog1("reservations file: %s (created)", _reservations_path->cString());\r
5733             _reservations = Dictionary::alloc()->initWithCapacity(0);\r
5734         }\r
5735         else\r
5736         {\r
5737             DebugLog1("reservations file: %s", _reservations_path->cString());\r
5738 \r
5739             // 予約情報シーケンスID\r
5740             _reservation_seq_id = _reservations->integerForKey(KEY_EPG_LAST_RESV_ID);\r
5741         }\r
5742 \r
5743         // ストリーミング制御情報格納用\r
5744         _streaming_ctrls = Dictionary::alloc()->initWithCapacity(0);\r
5745 \r
5746         // httpdのルートパス\r
5747         String *rootPath = _system_path->stringByAppendingPathComponent("iptd_html");\r
5748         if (!fm->fileExistsAtPath(rootPath, &isDir))\r
5749         {\r
5750             isDir = false;\r
5751         }\r
5752         if (!isDir)\r
5753         {\r
5754             DebugLog0("error: \"%s\" is not exists.", rootPath->cString());\r
5755             result = -1;\r
5756             break;\r
5757         }\r
5758 \r
5759         // httpd開始\r
5760         int port = _props->integerForKey(KEY_HTTP_PORT);\r
5761         _httpd = NET::HTTPDaemon::alloc()->initWithPort(port, 10);\r
5762         _httpd->setRootPath(rootPath);\r
5763         _httpd->setDelegate(this);\r
5764         if (!_httpd->start())\r
5765         {\r
5766             DebugLog0("Can't start httpd.");\r
5767             result = -1;\r
5768             break;\r
5769         }\r
5770 \r
5771         // 再開タイマ起動\r
5772         _timer_restart = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_RESTART, false);\r
5773         _timer_restart->fire();\r
5774 \r
5775         break;\r
5776     }\r
5777 \r
5778     // 初期化エラーがなければイベント待ちへ\r
5779     if (result == 0)\r
5780     {\r
5781         result = Application::start();\r
5782 \r
5783         // httpd終了待ち\r
5784         _httpd->stop();\r
5785     }\r
5786 \r
5787     RaymLock(this);\r
5788     _cancel_epg_collect = true;\r
5789     RaymUnlock(this);\r
5790 \r
5791     // タイマ停止\r
5792     if ((_timer_restart != NULL) && _timer_restart->valid())\r
5793     {\r
5794         _timer_restart->invalidate();\r
5795     }\r
5796     if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
5797     {\r
5798         _timer_epg_s->invalidate();\r
5799     }\r
5800     if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
5801     {\r
5802         _timer_epg_t->invalidate();\r
5803     }\r
5804     if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
5805     {\r
5806         _timer_periodic->invalidate();\r
5807     }\r
5808     if ((_timer_periodic_2 != NULL) && _timer_periodic_2->valid())\r
5809     {\r
5810         _timer_periodic_2->invalidate();\r
5811     }\r
5812 \r
5813     // チューナ解放\r
5814     for (int i = 0; i < _tunerCount; ++i)\r
5815     {\r
5816         if (_tuners[i] != NULL)\r
5817         {\r
5818             delete _tuners[i];\r
5819             _tuners[i] = NULL;\r
5820         }\r
5821     }\r
5822 \r
5823     // スケジュールリセット\r
5824     resetWakeSchedule();\r
5825 \r
5826     // 解放\r
5827     RELEASE(_timer_restart);\r
5828     RELEASE(_timer_epg_s);\r
5829     RELEASE(_timer_epg_t);\r
5830     RELEASE(_timer_periodic);\r
5831     RELEASE(_timer_periodic_2);\r
5832     RELEASE(_streaming_ctrls);\r
5833     RELEASE(_httpd);\r
5834     RELEASE(_system_path);\r
5835     RELEASE(_props_path);\r
5836     RELEASE(_props);\r
5837     RELEASE(_status_path);\r
5838     RELEASE(_status);\r
5839     RELEASE(_epgs_path);\r
5840     RELEASE(_epgs);\r
5841     RELEASE(_store_path);\r
5842     RELEASE(_reservations_path);\r
5843     RELEASE(_reservations);\r
5844 \r
5845     if (_multi2_dll != NULL)\r
5846     {\r
5847         FreeLibrary(_multi2_dll);\r
5848     }\r
5849 \r
5850     pool->release();\r
5851 \r
5852     // 終了\r
5853     DebugLog0("finished.");\r
5854 \r
5855 #ifdef RAYM_MEMORY_CHECK\r
5856     DebugLog0("global_raym_count_ = %d", Raym::global_raym_count_);\r
5857 #endif\r
5858 \r
5859     return result;\r
5860 }\r
5861 \r
5862 #ifndef _WIN32\r
5863 #pragma mark '\r
5864 #pragma mark ------- コンストラクタ/デストラクタ -------\r
5865 #endif\r
5866 \r
5867 Controller::Controller()\r
5868 {\r
5869 }\r
5870 \r
5871 Controller::~Controller()\r
5872 {\r
5873 }\r
5874 \r
5875 Controller *Controller::alloc()\r
5876 {\r
5877     return new Controller();\r
5878 }\r
5879 \r
5880 #ifndef _WIN32\r
5881 #pragma mark '\r
5882 #pragma mark ------- その他 -------\r
5883 #endif\r
5884 \r
5885 //\r
5886 // HH:MM:SS 形式の文字列から time_t に変換\r
5887 // 現在時刻が指定文字列の時刻以前の場合、当日の時刻。指定文字列の時刻よりも過ぎている場合、翌日の時刻を返す。\r
5888 //\r
5889 void Controller::getTimeWithString(String *str, time_t *time_var)\r
5890 {\r
5891     if ((str != NULL) && str->isMatch(String::stringWithUTF8String("^\\d\\d:\\d\\d:\\d\\d$")) && (time_var != NULL))\r
5892     {\r
5893         // 時:分:秒 を int型に分解\r
5894         std::string time_str = str->cString();\r
5895         int hour = atoi(time_str.substr(0, 2).c_str());\r
5896         int min  = atoi(time_str.substr(3, 2).c_str());\r
5897         int sec  = atoi(time_str.substr(6, 2).c_str());\r
5898         DebugLog2("%02d:%02d:%02d", hour, min, sec);\r
5899 \r
5900         // 現在時刻取得\r
5901         time_t now = time(NULL);\r
5902         TM now_tm;\r
5903         if (localtime_s(&now_tm, &now) == 0)\r
5904         {\r
5905             int now_sec = now_tm.tm_hour * 3600 + now_tm.tm_min * 60 + now_tm.tm_sec;\r
5906             int col_sec = hour * 3600 + min * 3600 + sec;\r
5907             if (now_sec > col_sec)\r
5908             {\r
5909                 ++now_tm.tm_mday;\r
5910             }\r
5911             now_tm.tm_hour = hour;\r
5912             now_tm.tm_min  = min;\r
5913             now_tm.tm_sec  = sec;\r
5914 \r
5915             *time_var = mktime(&now_tm);\r
5916         }\r
5917     }\r
5918 }\r
5919 \r
5920 void Controller::getTimeWithEPG(Dictionary *epg, time_t *start, time_t *end)\r
5921 {\r
5922     if ((epg == NULL) || (start == NULL) || (end == NULL))\r
5923     {\r
5924         return;\r
5925     }\r
5926     String *date = epg->stringForKey(KEY_EPG_DATE);\r
5927     String *st   = epg->stringForKey(KEY_EPG_START);\r
5928     String *ed   = epg->stringForKey(KEY_EPG_END);\r
5929     if ((date == NULL) || (st == NULL) || (ed == NULL))\r
5930     {\r
5931         return;\r
5932     }\r
5933 \r
5934     std::string dateStr = date->cString();\r
5935     std::string stStr = st->cString();\r
5936     std::string edStr = ed->cString();\r
5937     TM tm_start;\r
5938     tm_start.tm_year = atoi(dateStr.substr(0, 4).c_str()) - 1900;\r
5939     tm_start.tm_mon  = atoi(dateStr.substr(5, 2).c_str()) - 1;\r
5940     tm_start.tm_mday = atoi(dateStr.substr(8, 2).c_str());\r
5941     tm_start.tm_hour = atoi(stStr.substr(0, 2).c_str());\r
5942     tm_start.tm_min  = atoi(stStr.substr(3, 2).c_str());\r
5943     tm_start.tm_sec  = atoi(stStr.substr(6, 2).c_str());\r
5944 \r
5945     TM tm_end;\r
5946     tm_end.tm_year = atoi(dateStr.substr(0, 4).c_str()) - 1900;\r
5947     tm_end.tm_mon  = atoi(dateStr.substr(5, 2).c_str()) - 1;\r
5948     tm_end.tm_mday = atoi(dateStr.substr(8, 2).c_str());\r
5949     tm_end.tm_hour = atoi(edStr.substr(0, 2).c_str());\r
5950     tm_end.tm_min  = atoi(edStr.substr(3, 2).c_str());\r
5951     tm_end.tm_sec  = atoi(edStr.substr(6, 2).c_str());\r
5952     if (stStr > edStr)\r
5953     {\r
5954         tm_end.tm_mday += 1;\r
5955     }\r
5956     *start = mktime(&tm_start);\r
5957     *end = mktime(&tm_end);\r
5958 }\r
5959 \r
5960 } // iPTd\r
5961 } // ry0\r
5962 \r