OSDN Git Service

HLS試作
[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 #ifndef _WIN32\r
1538 #pragma mark '\r
1539 #pragma mark ------- システム関連 周期処理 -------\r
1540 #endif\r
1541 \r
1542 //\r
1543 // チューナ(streaming)制御/システム関連 周期処理\r
1544 //\r
1545 void Controller::periodic_2(void)\r
1546 {\r
1547     //\r
1548     // UDPポート監視\r
1549     //\r
1550 \r
1551     // マッピング(UDPPort:tuner,ch)情報取得\r
1552     Dictionary *mapping = NULL;\r
1553     if ((_streaming_ctrls != NULL) && ((mapping = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL)) != NULL))\r
1554     {\r
1555         // マッピング情報取得OK\r
1556 \r
1557         // 使用中のUDPの情報を取得\r
1558         // どれだけ使用中なのか不明なので、まずは必要サイズを調べる\r
1559         DWORD size = 0;\r
1560         if (GetExtendedUdpTable(NULL, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == ERROR_INSUFFICIENT_BUFFER)\r
1561         {\r
1562             // ERROR_INSUFFICIENT_BUFFER の場合、必要なバッファサイズが size に格納される\r
1563 \r
1564             // バッファ確保\r
1565             PMIB_UDPTABLE_OWNER_PID udptable = (PMIB_UDPTABLE_OWNER_PID)malloc(size);\r
1566             if (udptable != NULL)\r
1567             {\r
1568                 // バッファ確保OK\r
1569 \r
1570                 // UDP情報取得\r
1571                 if (GetExtendedUdpTable(udptable, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == NO_ERROR)\r
1572                 {\r
1573                     // 取得OK\r
1574                     DebugLog3("udptable->dwNumEntries: %d", udptable->dwNumEntries);\r
1575 \r
1576                     // 停止要否確認\r
1577                     Dictionary *using_port = _streaming_ctrls->dictionaryForKey(KEY_UDP_IN_USE);\r
1578                     if (using_port != NULL)\r
1579                     {\r
1580                         // key = 使用中ポート\r
1581                         Array *using_ports = using_port->allKeys();\r
1582                         if (using_ports != NULL)\r
1583                         {\r
1584                             // 使用中ポートでループ\r
1585                             for (uint i = 0; i < using_ports->count(); ++i)\r
1586                             {\r
1587                                 // 停止要否フラグ\r
1588                                 bool stop_need = true;\r
1589 \r
1590                                 // 使用中のUDP情報でループ\r
1591                                 for (uint j = 0; j < udptable->dwNumEntries; ++j)\r
1592                                 {\r
1593                                     if (((String *)using_ports->objectAtIndex(i))->intValue() == ntohs((WORD)udptable->table[j].dwLocalPort))\r
1594                                     {\r
1595                                         // 使用中なので停止不要\r
1596                                         stop_need = false;\r
1597                                         break;\r
1598                                     }\r
1599                                 }\r
1600 \r
1601                                 // 停止要否\r
1602                                 if (stop_need)\r
1603                                 {\r
1604                                     // マッピング情報を取得\r
1605                                     String *tuner_and_channel = mapping->stringForKey((String *)using_ports->objectAtIndex(i));\r
1606                                     if (tuner_and_channel != NULL)\r
1607                                     {\r
1608                                          // チューナとチャンネルに分割\r
1609                                         Range r = tuner_and_channel->rangeOfString(",");\r
1610                                         if (r.location != NotFound)\r
1611                                         {\r
1612                                             int tuner = tuner_and_channel->substringToIndex(r.location)->intValue();\r
1613                                             int channel = tuner_and_channel->substringFromIndex(r.location + 1)->intValue();\r
1614                                             DebugLog3("tuner: %d, channel: %d", tuner, channel);\r
1615 \r
1616                                             DebugLog0("auto streaming stop: %s", ((String *)using_ports->objectAtIndex(i))->cString());\r
1617 \r
1618                                             _tuners[tuner]->stopStreaming();\r
1619                                             using_port->removeObjectForKey((String *)using_ports->objectAtIndex(i));\r
1620                                         }\r
1621                                     }\r
1622                                 }\r
1623                             }\r
1624                         }\r
1625                     }\r
1626 \r
1627 \r
1628                     // 起動要否確認\r
1629                     for (uint i = 0; i < udptable->dwNumEntries; ++i)\r
1630                     {\r
1631                         // ポート番号を文字列に変換して\r
1632                         char port[10];\r
1633                         sprintf_s(port, "%d", ntohs((WORD)udptable->table[i].dwLocalPort));\r
1634                         DebugLog3("port = %s", port);\r
1635 \r
1636                         // マッピング情報を取得\r
1637                         String *tuner_and_channel = mapping->stringForKey(port);\r
1638                         if (tuner_and_channel != NULL)\r
1639                         {\r
1640                             // 取得OK: 監視対象ポートが使用されている\r
1641 \r
1642                             // 使用アプリを調べる\r
1643                             bool auto_streaming = false;\r
1644                             char exec_path[MAX_PATH];\r
1645                             memset(exec_path, 0, sizeof(exec_path));\r
1646 \r
1647                             // プロセスハンドル取得\r
1648                             size_t returnValue;\r
1649                             HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, udptable->table[i].dwOwningPid);\r
1650                             if (hProcess != NULL)\r
1651                             {\r
1652                                 TCHAR exec[MAX_PATH];\r
1653                                 memset(exec, 0, sizeof(exec));\r
1654                                 DWORD len = sizeof(exec) - 1;\r
1655 \r
1656                                 // イメージ取得\r
1657                                 if (QueryFullProcessImageName(hProcess, 0, exec, &len))\r
1658                                 {\r
1659                                     // ワイド -> マルチ 変換\r
1660                                     if (wcstombs_s(&returnValue, exec_path, sizeof(exec_path), exec, _TRUNCATE) == 0)\r
1661                                     {\r
1662                                         // 成功\r
1663 \r
1664                                         // とりあえず、、、現状は "ffmpeg.exe" / "vlc.exe" / "Kodi.exe" があったら auto_streaming を true にする\r
1665                                         if ((strstr(exec_path, "ffmpeg.exe") != NULL) ||\r
1666                                             (strstr(exec_path, "vlc.exe") != NULL) ||\r
1667                                             (strstr(exec_path, "Kodi.exe") != NULL))\r
1668                                         {\r
1669                                             auto_streaming = true;\r
1670                                         }\r
1671                                     }\r
1672                                 }\r
1673 \r
1674                                 // プロセスハンドル解放\r
1675                                 CloseHandle(hProcess);\r
1676                             }\r
1677 \r
1678                             if (auto_streaming)\r
1679                             {\r
1680                                 // チューナとチャンネルに分割\r
1681                                 Range r = tuner_and_channel->rangeOfString(",");\r
1682                                 if (r.location != NotFound)\r
1683                                 {\r
1684                                     int tuner = tuner_and_channel->substringToIndex(r.location)->intValue();\r
1685                                     int channel = tuner_and_channel->substringFromIndex(r.location + 1)->intValue();\r
1686                                     DebugLog3("tuner: %d, channel: %d", tuner, channel);\r
1687 \r
1688                                     // lock\r
1689                                     RaymLock(this);\r
1690 \r
1691                                     // 非ストリーミング中 かつ 非レコーディング中 または チャンネルが同じ 場合\r
1692                                     if (!_tuners[tuner]->isStreaming() && (!_tuners[tuner]->isRecording() || _tuners[tuner]->channel() == channel))\r
1693                                     {\r
1694                                         // ストリーミング開始可能\r
1695 \r
1696                                         if (_tuners[tuner]->channel() != channel)\r
1697                                         {\r
1698                                             setChannel(tuner, channel);\r
1699                                         }\r
1700 \r
1701                                         SOCKADDR_IN dst_addr;\r
1702                                         dst_addr.sin_family = AF_INET;\r
1703                                         dst_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r
1704                                         dst_addr.sin_port = (WORD)udptable->table[i].dwLocalPort;\r
1705 \r
1706                                         if (_tuners[tuner]->startStreaming(&dst_addr))\r
1707                                         {\r
1708                                             // 成功\r
1709                                             DebugLog0("auto streaming start: %d", ntohs((WORD)udptable->table[i].dwLocalPort));\r
1710 \r
1711                                             // 使用中ポートに登録\r
1712                                             using_port = _streaming_ctrls->dictionaryForKey(KEY_UDP_IN_USE);\r
1713                                             if (using_port == NULL)\r
1714                                             {\r
1715                                                 using_port = Dictionary::dictionaryWithCapacity(0);\r
1716                                                 _streaming_ctrls->setObject(using_port, KEY_UDP_IN_USE);\r
1717                                             }\r
1718                                             using_port->setBool(true, port);\r
1719                                         }\r
1720                                     }\r
1721 \r
1722                                     // unlock\r
1723                                     RaymUnlock(this);\r
1724                                 }\r
1725                             }\r
1726                         }\r
1727                     }\r
1728                 }\r
1729 \r
1730                 // バッファ解放\r
1731                 free(udptable);\r
1732             }\r
1733         }\r
1734     }\r
1735 \r
1736     //\r
1737     // HLS\r
1738     //\r
1739     Dictionary *hls_info = _streaming_ctrls->dictionaryForKey(KEY_HLS_INFO);\r
1740     if (hls_info != NULL)\r
1741     {\r
1742         for (int i = 0; i < _tunerCount; ++i)\r
1743         {\r
1744             RaymLock(this);\r
1745             Dictionary *hls_info_tuner = hls_info->dictionaryForKey(_tuners[i]->name());\r
1746             if (hls_info_tuner != NULL)\r
1747             {\r
1748                 int counter = hls_info_tuner->integerForKey(KEY_COUNTER);\r
1749                 if (counter < 60)\r
1750                 {\r
1751                     hls_info_tuner->setInteger(counter + 1, KEY_COUNTER);\r
1752                 }\r
1753                 else\r
1754                 {\r
1755                     hls_info->removeObjectForKey(_tuners[i]->name());\r
1756                 }\r
1757             }\r
1758             RaymUnlock(this);\r
1759         }\r
1760     }\r
1761 \r
1762 \r
1763     //\r
1764     // 1/100秒単位が 0 に近くなるように次回T.O.を微調整\r
1765     //\r
1766 \r
1767 #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)\r
1768     static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64;\r
1769 #else\r
1770     static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL;\r
1771 #endif\r
1772     // 現在時刻を取得\r
1773     FILETIME ft;\r
1774     GetSystemTimeAsFileTime(&ft);\r
1775 \r
1776     // EPOCH秒への変換\r
1777     __time64_t now_sec;\r
1778     __time64_t now_usec;\r
1779     now_sec = ft.dwHighDateTime;\r
1780     now_sec <<= 32;\r
1781     now_sec |= ft.dwLowDateTime;\r
1782     now_sec /= 10;  /*convert into microseconds*/\r
1783     now_sec -= DELTA_EPOCH_IN_MICROSECS;\r
1784     now_usec = (now_sec % 1000000UL);\r
1785     now_sec = now_sec / 1000000UL;\r
1786 \r
1787     TimeInterval interval = (TimeInterval)now_usec;\r
1788     interval = interval / 1000000;\r
1789     _timer_periodic_2->setTimeInterval(1.005 - interval);\r
1790 }\r
1791 \r
1792 #ifndef _WIN32\r
1793 #pragma mark '\r
1794 #pragma mark ------- HTTP制御 -------\r
1795 #endif\r
1796 \r
1797 static std::string epg_regist_form(Dictionary *epg)\r
1798 {\r
1799     DebugLog3("epg_regist_form() start.");\r
1800 \r
1801     std::string epgs;\r
1802 \r
1803     if ((epg != NULL) &&\r
1804         (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
1805         (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
1806         (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
1807         (epg->stringForKey(KEY_EPG_END) != NULL) &&\r
1808         (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
1809         (epg->stringForKey(KEY_EPG_TITLE) != NULL))\r
1810     {\r
1811         epgs += "<form id=\"";\r
1812         epgs += "epg_";\r
1813         epgs += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
1814         epgs += "_";\r
1815         epgs += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
1816         epgs += "\" title=\"";\r
1817         epgs += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
1818         epgs += "-";\r
1819         epgs += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
1820         epgs += "\" class=\"panel\" action=\"regist.cgi\" method=\"GET\" target=\"_self\" onclick='return confirm(\"Is it ok?\");'>";\r
1821 \r
1822         // 放送時間\r
1823         epgs += "<h2>";\r
1824         epgs += LocalizedString(KEY_I18N_Broadcasting_Time, NULL)->cString();\r
1825         epgs += "</h2>";\r
1826         epgs += "<fieldset>";\r
1827         epgs += "<p class=\"normalText\">&nbsp;&nbsp;";\r
1828         epgs += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
1829         epgs += "&nbsp;";\r
1830         epgs += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
1831         epgs += "-";\r
1832         epgs += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
1833         epgs += "</p>";\r
1834         epgs += "</fieldset>";\r
1835 \r
1836         // 番組名\r
1837         epgs += "<h2>";\r
1838         epgs += LocalizedString(KEY_I18N_Program_Title, NULL)->cString();\r
1839         epgs += "</h2>";\r
1840         epgs += "<fieldset>";\r
1841         epgs += "<p class=\"normalText\">&nbsp;&nbsp;";\r
1842         epgs += epg->stringForKey(KEY_EPG_TITLE)->cString();\r
1843         epgs += "</p>";\r
1844         epgs += "</fieldset>";\r
1845 \r
1846         // 概要\r
1847         if (epg->stringForKey(KEY_EPG_DESCRIPTION) != NULL)\r
1848         {\r
1849             epgs += "<h2>";\r
1850             epgs += LocalizedString(KEY_I18N_Description, NULL)->cString();\r
1851             epgs += "</h2>";\r
1852             epgs += "<fieldset>";\r
1853             epgs += "<p class=\"normalText\">&nbsp;&nbsp;";\r
1854             epgs += epg->stringForKey(KEY_EPG_DESCRIPTION)->cString();\r
1855             epgs += "</p>";\r
1856             epgs += "</fieldset>";\r
1857         }\r
1858 \r
1859         //\r
1860         epgs += "<input type=\"hidden\" name=\"service_id\" value=\"";\r
1861         epgs += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
1862         epgs += "\"/>";\r
1863         epgs += "<input type=\"hidden\" name=\"event_id\" value=\"";\r
1864         epgs += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
1865         epgs += "\"/>";\r
1866         epgs += "<input class=\"redButton\" type=\"submit\" value=\"";\r
1867         epgs += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
1868         epgs += "\"/>";\r
1869     //    epgs += "<a class=\"redButton\" type=\"submit\">";\r
1870     //    epgs += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
1871     //    epgs += "</a>";\r
1872         epgs += "</form>";\r
1873     }\r
1874 \r
1875     return epgs;\r
1876 }\r
1877 \r
1878 HTTPResponse *responseWithDictionary(HTTPRequest *request, Dictionary *dictionary)\r
1879 {\r
1880     HTTPResponse *result = NULL;\r
1881     if ((request != NULL) && (dictionary != NULL))\r
1882     {\r
1883         std::string xml = dictionary->toString();\r
1884 \r
1885         // header\r
1886         InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
1887         // Date\r
1888         // Server\r
1889         // Content-Encoding\r
1890         // Last-Modified\r
1891         // Content-Type\r
1892         header->setFieldBodyWithName("application/xml", "Content-Type");\r
1893         // Connection\r
1894         // Tranfer-Encoding\r
1895         // Content-Length\r
1896         header->setFieldBodyWithName(String::stringWithFormat("%I64u", xml.length()), "Content-Length");\r
1897 \r
1898         // body\r
1899         InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(String::stringWithUTF8String(xml.c_str()));\r
1900 \r
1901         // message\r
1902         InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
1903         RELEASE(header);\r
1904         RELEASE(body);\r
1905         if (message != NULL)\r
1906         {\r
1907             result = HTTPResponse::alloc()->init();\r
1908             result->autorelease();\r
1909             result->setVersion(request->version());\r
1910             result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
1911             result->setStatus(200);\r
1912             result->setMessage(message);\r
1913             RELEASE(message);\r
1914         }\r
1915     }\r
1916     return result;\r
1917 }\r
1918 \r
1919 // positive response by XML\r
1920 HTTPResponse *responseForSuccess(HTTPRequest *request)\r
1921 {\r
1922     Dictionary *dict = Dictionary::dictionaryWithCapacity(0);\r
1923     dict->setString("Success", KEY_RESULT);\r
1924     return responseWithDictionary(request, dict);\r
1925 }\r
1926 \r
1927 // negative response by XML\r
1928 HTTPResponse *responseForFailed(HTTPRequest *request)\r
1929 {\r
1930     Dictionary *dict = Dictionary::dictionaryWithCapacity(0);\r
1931     dict->setString("Failed", KEY_RESULT);\r
1932     return responseWithDictionary(request, dict);\r
1933 }\r
1934 \r
1935 HTTPResponse *Controller::responseWithHTML(HTTPRequest *request, String *html)\r
1936 {\r
1937     HTTPResponse *result = NULL;\r
1938     if ((html != NULL) && (request != NULL))\r
1939     {\r
1940         // header\r
1941         InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
1942         // Date\r
1943         // Server\r
1944         // Content-Encoding\r
1945         // Last-Modified\r
1946         // Content-Type\r
1947         header->setFieldBodyWithName("text/html", "Content-Type");\r
1948         // Connection\r
1949         // Tranfer-Encoding\r
1950         // Content-Length\r
1951         header->setFieldBodyWithName(String::stringWithFormat("%I64u", html->length()), "Content-Length");\r
1952 \r
1953         // body\r
1954         InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(html);\r
1955 \r
1956         // message\r
1957         InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
1958         RELEASE(header);\r
1959         RELEASE(body);\r
1960         if (message != NULL)\r
1961         {\r
1962 //            result = HTTPResponse::response();\r
1963             result = HTTPResponse::alloc()->init();\r
1964             result->setVersion(request->version());\r
1965             result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
1966             result->setStatus(200);\r
1967             result->setMessage(message);\r
1968             result->autorelease();\r
1969             RELEASE(message);\r
1970         }\r
1971     }\r
1972     return result;\r
1973 }\r
1974 \r
1975 HTTPResponse *Controller::responseWithUTF8Text(HTTPRequest *request, String *text)\r
1976 {\r
1977     HTTPResponse *result = NULL;\r
1978     if ((text != NULL) && (request != NULL))\r
1979     {\r
1980         // header\r
1981         InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
1982         // Date\r
1983         // Server\r
1984         // Content-Encoding\r
1985         // Last-Modified\r
1986         // Content-Type\r
1987         header->setFieldBodyWithName("text/plane; charset=UTF-8", "Content-Type");\r
1988         // Connection\r
1989         // Tranfer-Encoding\r
1990         // Content-Length\r
1991         header->setFieldBodyWithName(String::stringWithFormat("%I64u", text->length()), "Content-Length");\r
1992 \r
1993         // body\r
1994         InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(text);\r
1995 \r
1996         // message\r
1997         InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
1998         RELEASE(header);\r
1999         RELEASE(body);\r
2000         if (message != NULL)\r
2001         {\r
2002 //            result = HTTPResponse::response();\r
2003             result = HTTPResponse::alloc()->init();\r
2004             result->setVersion(request->version());\r
2005             result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
2006             result->setStatus(200);\r
2007             result->setMessage(message);\r
2008             result->autorelease();\r
2009             RELEASE(message);\r
2010         }\r
2011     }\r
2012     return result;\r
2013 }\r
2014 \r
2015 HTTPResponse *Controller::responseByResultAndReferer(HTTPRequest *request, bool result, const char *referer)\r
2016 {\r
2017     HTTPResponse *retval = NULL;\r
2018 \r
2019     bool iui = false;\r
2020 \r
2021     String *ref = NULL;\r
2022     InternetTextMessage *msg = request->message();\r
2023     if (msg != NULL)\r
2024     {\r
2025         InternetTextMessageHeader *header = msg->header();\r
2026         if (header != NULL)\r
2027         {\r
2028             String *field_referer = header->fieldBodyForName("Referer");\r
2029             if (field_referer != NULL)\r
2030             {\r
2031                 String *field_host    = header->fieldBodyForName("Host");\r
2032                 if (field_host != NULL)\r
2033                 {\r
2034                     std::string tmp = "http://";\r
2035                     tmp += field_host->cString();\r
2036                     tmp += "/";\r
2037                     tmp += referer;\r
2038                     iui = field_referer->isEqualToString(tmp.c_str());\r
2039                     DebugLog2("tmp: %s", tmp.c_str());\r
2040                     DebugLog2("ref: %s", field_referer->cString());\r
2041                 }\r
2042             }\r
2043         }\r
2044     }\r
2045 //    if ((ref != NULL) && match(ref->cString(), referer))\r
2046 //    if ((ref != NULL) && ref->isEqualToString(referer))\r
2047     if (iui)\r
2048     {\r
2049         String *path = _httpd->rootPath()->stringByAppendingPathComponent("template2.html");\r
2050         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
2051         if (html != NULL)\r
2052         {\r
2053             html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", "Result");\r
2054             std::string contents;\r
2055             std::string reservations;\r
2056             contents += "<div id=\"home\" class=\"panel\" title=\"Result\" selected=\"true\">";\r
2057             if (result)\r
2058             {\r
2059                 contents += "<h2>Success</h2>";\r
2060             }\r
2061             else\r
2062             {\r
2063                 contents += "<h2>Failed</h2>";\r
2064             }\r
2065             contents += "</div>";\r
2066 \r
2067             html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
2068             retval = responseWithHTML(request, html);\r
2069         }\r
2070     }\r
2071     else\r
2072     {\r
2073         if (result)\r
2074         {\r
2075             retval = responseForSuccess(request);\r
2076         }\r
2077         else\r
2078         {\r
2079             retval = responseForFailed(request);\r
2080         }\r
2081     }\r
2082 \r
2083     return retval;\r
2084 }\r
2085 \r
2086 HTTPResponse *Controller::responseForMain(HTTPRequest *request, SOCKADDR_IN *client)\r
2087 {\r
2088     DebugLog2("Controller::responseForMain()");\r
2089 \r
2090     HTTPResponse *result = NULL;\r
2091     while ((request != NULL) && (client != NULL))\r
2092     {\r
2093         String *path = _httpd->rootPath();\r
2094         if (path == NULL)\r
2095         {\r
2096             DebugLog3("_httpd->rootPath() ng.");\r
2097             break;\r
2098         }\r
2099 \r
2100         path = path->stringByAppendingPathComponent("template1.html");\r
2101         if (path == NULL)\r
2102         {\r
2103             DebugLog3("path->stringByAppendingPathComponent() ng.");\r
2104             break;\r
2105         }\r
2106 \r
2107         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
2108         if (html == NULL)\r
2109         {\r
2110             DebugLog3("String::stringWithContentsOfFile() ng.");\r
2111             break;\r
2112         }\r
2113 \r
2114         String *server_name = _props->stringForKey(KEY_NAME);\r
2115         if (server_name == NULL)\r
2116         {\r
2117             DebugLog3("_props->stringForKey(KEY_NAME) ng.");\r
2118             break;\r
2119         }\r
2120 \r
2121         html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", server_name);\r
2122         if (html == NULL)\r
2123         {\r
2124             DebugLog3("html->stringByReplacingOccurrencesOfString() ng.");\r
2125             break;\r
2126         }\r
2127 \r
2128         html = html->stringByReplacingOccurrencesOfString("%%PAGE_TITLE%%", LocalizedString(KEY_I18N_Main_Menu, NULL));\r
2129         if (html == NULL)\r
2130         {\r
2131             DebugLog3("html->stringByReplacingOccurrencesOfString() ng.");\r
2132         }\r
2133 \r
2134         std::string contents;\r
2135         contents += "<div id=\"home\" class=\"panel\" selected=\"true\">";\r
2136         contents += "<ul>";\r
2137         contents += "<li><a target=\"_self\" href=\"/programs.html\">";\r
2138         contents += LocalizedString(KEY_I18N_Programs, NULL)->cString();\r
2139         contents += "</a></li>";\r
2140         contents += "<li><a target=\"_self\" href=\"/tv.html\">";\r
2141         contents += LocalizedString(KEY_I18N_TV, NULL)->cString();\r
2142         contents += "</a></li>";\r
2143         contents += "<li><a target=\"_self\" href=\"/video.html\">";\r
2144         contents += LocalizedString(KEY_I18N_Video, NULL)->cString();\r
2145         contents += "</a></li>";\r
2146         contents += "<li><a target=\"_self\" href=\"/reservation.html\">";\r
2147         contents += LocalizedString(KEY_I18N_Reservation, NULL)->cString();\r
2148         contents += "</a></li>";\r
2149         contents += "</ul>";\r
2150 #if 0\r
2151         contents += "<ul>";\r
2152         contents += "<li><a target=\"_self\" href=\"/exec_vlc.html\">VLC media player";\r
2153         contents += "</ul>";\r
2154 #endif\r
2155         contents += "<ul>";\r
2156         contents += "<li><a target=\"_self\" href=\"/status.html\">";\r
2157         contents += LocalizedString(KEY_I18N_Tuner_Status, NULL)->cString();\r
2158         contents += "</a></li>";\r
2159         contents += "</ul>";\r
2160         contents += "<center><a target=\"_self\" href=\"/iptd.log\">Ver. ";\r
2161         contents += VERSION;\r
2162         contents += "</a></center>";\r
2163         contents += "</div>";\r
2164 \r
2165         html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
2166         if (html != NULL)\r
2167         {\r
2168             result = responseWithHTML(request, html);\r
2169         }\r
2170 \r
2171         break;\r
2172     }\r
2173 \r
2174     return result;\r
2175 }\r
2176 \r
2177 HTTPResponse *Controller::responseForPrograms(HTTPRequest *request, SOCKADDR_IN *client)\r
2178 {\r
2179     DebugLog2("Controller::responseForPrograms() start.");\r
2180 \r
2181     HTTPResponse *result = NULL;\r
2182     while ((request != NULL) && (client != NULL))\r
2183     {\r
2184         String *path = _httpd->rootPath();\r
2185         if (path == NULL)\r
2186         {\r
2187             DebugLog3("_httpd->rootPath() ng.");\r
2188             break;\r
2189         }\r
2190 \r
2191         path = path->stringByAppendingPathComponent("template2.html");\r
2192         if (path == NULL)\r
2193         {\r
2194             DebugLog3("path->stringByAppendingPathComponent() ng.");\r
2195             break;\r
2196         }\r
2197 \r
2198         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
2199         if (html == NULL)\r
2200         {\r
2201             DebugLog3("String::stringWithContentsOfFile() ng.");\r
2202             break;\r
2203         }\r
2204 \r
2205         html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Programs, NULL));\r
2206         if (html == NULL)\r
2207         {\r
2208             DebugLog3("html->stringByReplacingOccurrencesOfString() ng.");\r
2209             break;\r
2210         }\r
2211 \r
2212         std::string contents;\r
2213         std::string epgs;\r
2214 \r
2215         contents += "<ul id=\"home\" title=\"";\r
2216         contents += LocalizedString(KEY_I18N_Programs, NULL)->cString();\r
2217         contents += "\" selected=\"true\">";\r
2218         // 地デジ\r
2219         contents += "<li>";\r
2220         contents += "<a href=\"#isdb_t\">";\r
2221         contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
2222         contents += "</a></li>";\r
2223         // BS/CS\r
2224         contents += "<li>";\r
2225         contents += "<a href=\"#isdb_s\">";\r
2226         contents += "BS/CS";\r
2227         contents += "</a></li>";\r
2228 \r
2229         contents += "</ul>";\r
2230 \r
2231         // 地デジ(局名)\r
2232         contents += "<ul id=\"isdb_t\" title=\"";\r
2233         contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
2234         contents += "\">";\r
2235         Array *stations = stationInfos(Tuner::ISDB_T);\r
2236         for (uint i = 0; i < stations->count(); ++i)\r
2237         {\r
2238             Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2239             if ((station != NULL) &&\r
2240                 (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2241                 (station->stringForKey(KEY_NAME) != NULL))\r
2242             {\r
2243                 contents += "<li>";\r
2244                 contents += "<a href=\"#isdb_t_";\r
2245                 contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
2246                 contents += "\">";\r
2247                 contents += station->stringForKey(KEY_NAME)->cString();\r
2248                 contents += "</a></li>";\r
2249             }\r
2250         }\r
2251         contents += "</ul>";\r
2252 \r
2253         // 地デジ(番組データ)\r
2254         for (uint i = 0; i < stations->count(); ++i)\r
2255         {\r
2256             Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2257             if ((station != NULL) &&\r
2258                 (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2259                 (station->stringForKey(KEY_NAME) != NULL))\r
2260             {\r
2261 \r
2262                 contents += "<ul id=\"isdb_t_";\r
2263                 contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
2264                 contents += "\" title=\"";\r
2265                 contents += station->stringForKey(KEY_NAME)->cString();\r
2266                 contents += "\">";\r
2267 \r
2268                 Array *programs = programsForServices(station->arrayForKey(KEY_SERVICES));\r
2269                 for (uint i = 0; i < programs->count(); ++i)\r
2270                 {\r
2271                     Dictionary *epg = (Dictionary *)programs->objectAtIndex(i);\r
2272                     if ((epg != NULL) &&\r
2273                         (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
2274                         (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
2275                         (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
2276                         (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
2277                         (epg->stringForKey(KEY_EPG_END) != NULL))\r
2278                     {\r
2279                         contents += "<li>";\r
2280                         contents += "<a href=\"#epg_";\r
2281                         contents += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
2282                         contents += "_";\r
2283                         contents += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
2284                         contents += "\">";\r
2285                         contents += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
2286                         contents += "&nbsp;";\r
2287                         contents += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
2288                         contents += "-";\r
2289                         contents += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
2290                         contents += "</a></li>";\r
2291 \r
2292                         epgs += epg_regist_form(epg);\r
2293                     }\r
2294                 }\r
2295                 contents += "</ul>";\r
2296             }\r
2297         }\r
2298 \r
2299         // BS/CS(局名)\r
2300         contents += "<ul id=\"isdb_s\" title=\"";\r
2301         contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
2302         contents += "\">";\r
2303         stations = stationInfos(Tuner::ISDB_S);\r
2304         for (uint i = 0; i < stations->count(); ++i)\r
2305         {\r
2306             Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2307             if ((station != NULL) &&\r
2308                 (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2309                 (station->stringForKey(KEY_NAME) != NULL))\r
2310             {\r
2311                 contents += "<li>";\r
2312                 contents += "<a href=\"#isdb_s_";\r
2313                 contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
2314                 contents += "\">";\r
2315                 contents += station->stringForKey(KEY_NAME)->cString();\r
2316                 contents += "</a></li>";\r
2317             }\r
2318         }\r
2319         contents += "</ul>";\r
2320 \r
2321         // BS/CS(番組データ)\r
2322         for (uint i = 0; i < stations->count(); ++i)\r
2323         {\r
2324             Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2325             if ((station != NULL) &&\r
2326                 (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2327                 (station->stringForKey(KEY_NAME) != NULL))\r
2328             {\r
2329                 contents += "<ul id=\"isdb_s_";\r
2330                 contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
2331                 contents += "\" title=\"";\r
2332                 contents += station->stringForKey(KEY_NAME)->cString();\r
2333                 contents += "\">";\r
2334 \r
2335                 Array *programs = programsForServices(station->arrayForKey(KEY_SERVICES));\r
2336                 for (uint i = 0; i < programs->count(); ++i)\r
2337                 {\r
2338                     Dictionary *epg = (Dictionary *)programs->objectAtIndex(i);\r
2339                     if ((epg != NULL) &&\r
2340                         (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
2341                         (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
2342                         (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
2343                         (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
2344                         (epg->stringForKey(KEY_EPG_END) != NULL))\r
2345                     {\r
2346                         contents += "<li>";\r
2347                         contents += "<a href=\"#epg_";\r
2348                         contents += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
2349                         contents += "_";\r
2350                         contents += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
2351                         contents += "\">";\r
2352                         contents += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
2353                         contents += "&nbsp;";\r
2354                         contents += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
2355                         contents += "-";\r
2356                         contents += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
2357                         contents += "</a></li>";\r
2358 \r
2359                         epgs += epg_regist_form(epg);\r
2360                     }\r
2361                 }\r
2362                 contents += "</ul>";\r
2363             }\r
2364         }\r
2365 \r
2366         contents += epgs;\r
2367         html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
2368         if (html != NULL)\r
2369         {\r
2370             result = responseWithHTML(request, html);\r
2371         }\r
2372 \r
2373         break;\r
2374     }    \r
2375     return result;\r
2376 }\r
2377 \r
2378 HTTPResponse *Controller::responseForReservation(HTTPRequest *request, SOCKADDR_IN *client)\r
2379 {\r
2380     DebugLog2("Controller::responseForReservation() start.");\r
2381 \r
2382     // lock\r
2383     RaymLock(this);\r
2384 \r
2385     HTTPResponse *result = NULL;\r
2386     while ((request != NULL) && (client != NULL))\r
2387     {\r
2388         // create array of reservations sorted by seq_id.\r
2389         Array *array = Array::arrayWithCapacity(0);\r
2390         for (int i = 0; i < _tunerCount; ++i)\r
2391         {\r
2392             Array *tmp = _reservations->arrayForKey(_tuners[i]->name());\r
2393             if (tmp == NULL)\r
2394             {\r
2395                 continue;\r
2396             }\r
2397             for (uint idx = 0; idx < tmp->count(); ++idx)\r
2398             {\r
2399                 Dictionary *epg = (Dictionary *)tmp->objectAtIndex(idx);\r
2400                 if (epg == NULL)\r
2401                 {\r
2402                     continue;\r
2403                 }\r
2404 \r
2405                 time_t epg_start;\r
2406                 time_t tmp_end;\r
2407                 getTimeWithEPG(epg, &epg_start, &tmp_end);\r
2408 \r
2409                 bool inserted = false;\r
2410                 for (uint j = 0; j < array->count(); ++j)\r
2411                 {\r
2412                     Dictionary *epg2 = (Dictionary *)array->objectAtIndex(j);\r
2413                     if (epg2 == NULL)\r
2414                     {\r
2415                         continue;\r
2416                     }\r
2417 \r
2418                     time_t epg2_start;\r
2419                     getTimeWithEPG(epg2, &epg2_start, &tmp_end);\r
2420 \r
2421                     if (epg_start <= epg2_start)\r
2422                     {\r
2423                         array->insertObject(epg, j);\r
2424                         inserted = true;\r
2425                         break;\r
2426                     }\r
2427                 }\r
2428 \r
2429                 if (!inserted)\r
2430                 {\r
2431                     array->addObject(epg);\r
2432                 }\r
2433             }\r
2434         }\r
2435 \r
2436         String *path = _httpd->rootPath();\r
2437         if (path == NULL)\r
2438         {\r
2439             break;\r
2440         }\r
2441 \r
2442         path = path->stringByAppendingPathComponent("template2.html");\r
2443         if (path == NULL)\r
2444         {\r
2445             break;\r
2446         }\r
2447 \r
2448         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
2449         if (html == NULL)\r
2450         {\r
2451             break;\r
2452         }\r
2453 \r
2454         html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Reservation, NULL));\r
2455         if (html == NULL)\r
2456         {\r
2457             break;\r
2458         }\r
2459 \r
2460         std::string contents;\r
2461         std::string reservations;\r
2462         contents += "<ul id=\"home\" title=\"";\r
2463         contents += LocalizedString(KEY_I18N_Reservation, NULL)->cString();\r
2464         contents += "\" selected=\"true\">";\r
2465 \r
2466 #if 0\r
2467         contents += "<li><a href=\"#tuners\">";\r
2468         contents += "<font color=\"blue\">[";\r
2469         contents += LocalizedString(KEY_I18N_Tuner, NULL)->cString();\r
2470         contents += "]</font>";\r
2471         contents += "</a></li>";\r
2472 #endif\r
2473 \r
2474         //\r
2475         // "[新規予約]"\r
2476         //\r
2477         contents += "<li><a href=\"#new_reservation\">";\r
2478         contents += "<font color=\"red\">[";\r
2479         contents += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
2480         contents += "]</font>";\r
2481         contents += "</a></li>";\r
2482 \r
2483         //\r
2484         // "[キーワード]"\r
2485         //\r
2486         contents += "<li><a href=\"#keywords\">";\r
2487         contents += "<font color=\"blue\">[";\r
2488         contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
2489         contents += "]</font>";\r
2490         contents += "</a></li>";\r
2491 \r
2492         //\r
2493         // 登録済み予約情報\r
2494         //\r
2495         for (uint i = 0; i < array->count(); ++i)\r
2496         {\r
2497             Dictionary *epg = (Dictionary *)array->objectAtIndex(i);\r
2498             char seq_id[7];\r
2499             sprintf_s(seq_id, "%06d", epg->integerForKey(KEY_EPG_RESV_ID) % 1000000);\r
2500             contents += "<li>";\r
2501             contents += "<a href=\"#seqid";\r
2502             contents += seq_id;\r
2503             contents += "\">";\r
2504             String *title = epg->stringForKey(KEY_EPG_TITLE);\r
2505             if (title != NULL)\r
2506             {\r
2507                 contents += title->cString();\r
2508             }\r
2509             else\r
2510             {\r
2511                 time_t start;\r
2512                 time_t end;\r
2513                 Controller::getTimeWithEPG(epg, &start, &end);\r
2514                 TM tm;\r
2515                 if (localtime_s(&tm, &start) == 0)\r
2516                 {\r
2517                     char date_time[24];\r
2518                     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
2519                     contents += date_time;\r
2520                 }\r
2521                 else\r
2522                 {\r
2523                     contents += seq_id;\r
2524                 }\r
2525             }\r
2526             contents += "</a></li>";\r
2527 \r
2528             // reservations\r
2529             reservations += "<div id=\"";\r
2530             reservations += "seqid";\r
2531             reservations += seq_id;\r
2532             reservations += "\" title=\"";\r
2533             reservations += "xxxxxx";\r
2534             reservations += "\" class=\"panel\">";\r
2535 \r
2536             //reservations += "<h2>Status</h2>";\r
2537             reservations += "<fieldset>";\r
2538 \r
2539             //\r
2540             reservations += "<div class=\"row\">";\r
2541             reservations += "<label>Date</label>";\r
2542             reservations += "<span>";\r
2543             String *tmp = epg->stringForKey(KEY_EPG_DATE);\r
2544             reservations += tmp->cString();\r
2545             reservations += "</span>";\r
2546             reservations += "</div>";\r
2547 \r
2548             reservations += "<div class=\"row\">";\r
2549             reservations += "<label>Start</label>";\r
2550             reservations += "<span>";\r
2551             tmp = epg->stringForKey(KEY_EPG_START);\r
2552             reservations += tmp->cString();\r
2553             reservations += "</span>";\r
2554             reservations += "</div>";\r
2555 \r
2556             reservations += "<div class=\"row\">";\r
2557             reservations += "<label>End</label>";\r
2558             reservations += "<span>";\r
2559             tmp = epg->stringForKey(KEY_EPG_END);\r
2560             reservations += tmp->cString();\r
2561             reservations += "</span>";\r
2562             reservations += "</div>";\r
2563 \r
2564             reservations += "</fieldset>";\r
2565 \r
2566             reservations += "<a class=\"whiteButton\" type=\"submit\" href=\"/cancel.cgi?";\r
2567             reservations += "resv_id=";\r
2568             reservations += seq_id;\r
2569             reservations += "\" onclick='return confirm(\"To cancel. Is it ok?\");'>";\r
2570             reservations += LocalizedString(KEY_I18N_Cancel, NULL)->cString();\r
2571             reservations += "</a>";\r
2572 \r
2573             reservations += "</div>";\r
2574         }\r
2575 \r
2576         contents += "</ul>";\r
2577         contents += reservations;\r
2578 \r
2579         //\r
2580         // 新規予約:ここから\r
2581         //\r
2582 \r
2583         // 予約時間の初期値作成\r
2584         TM tm1, tm2;\r
2585         time_t time1 = time(NULL);\r
2586         time1 = (time1 + 15 * 60) / (15 * 60);\r
2587         time1 = time1 * 15 * 60;\r
2588         time_t time2 = time1 + 15 * 60;\r
2589         localtime_s(&tm1, &time1);\r
2590         localtime_s(&tm2, &time2);\r
2591         \r
2592         contents += "<form name=\"reservation\" id=\"new_reservation\" class=\"panel\" method=\"GET\" action=\"regist.cgi\" target=\"_self\">";\r
2593         contents += "<fieldset>";\r
2594         contents += "<div class=\"row\">";\r
2595         contents += "<label>";\r
2596         contents += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
2597         contents += " : </label>";\r
2598         contents += "<span>";\r
2599         contents += "<select name=\"service_id\" size=\"1\">";\r
2600 \r
2601         // 局情報の収集\r
2602         Array *stationInfos = Controller::stationInfos(Tuner::ISDB_T);\r
2603         if (stationInfos != NULL)\r
2604         {\r
2605             stationInfos->addObjectsFromArray(Controller::stationInfos(Tuner::ISDB_S));\r
2606         }\r
2607         else\r
2608         {\r
2609             stationInfos = Controller::stationInfos(Tuner::ISDB_S);\r
2610         }\r
2611         for (uint i = 0; (stationInfos != NULL) && (i < stationInfos->count()); ++i)\r
2612         {\r
2613             Array *services = ((Dictionary *)stationInfos->objectAtIndex(i))->arrayForKey(KEY_SERVICES);\r
2614 \r
2615 // とりえあず、現状は最初のサービスのみ表示\r
2616 //                for (uint j = 0; (services != NULL) &&  (j < services->count()); ++j)\r
2617             for (uint j = 0; (services != NULL) &&  (j < 1); ++j)\r
2618             {\r
2619                 Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
2620 \r
2621                 // テレビ放送かチェック\r
2622                 if ((service->stringForKey(KEY_SERVICE_TYPE) != NULL) && service->stringForKey(KEY_SERVICE_TYPE)->isEqualToString("1"))\r
2623                 {\r
2624                     String *name = service->stringForKey(KEY_NAME);\r
2625                     String *sid  = service->stringForKey(KEY_SERVICE_ID);\r
2626                     if ((name != NULL) && (sid != NULL))\r
2627                     {\r
2628                         String *tmp = String::stringWithFormat("<option value=\"%s\">%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
2629                         contents += tmp->cString();\r
2630                     }\r
2631                 }\r
2632             }\r
2633         }\r
2634 \r
2635         contents += "</select>";\r
2636         contents += "</span>";\r
2637         contents += "</div>";\r
2638         contents += "<div class=\"row\">";\r
2639         contents += "<label>";\r
2640         contents += LocalizedString(KEY_I18N_Date, NULL)->cString();\r
2641         contents += " : </label>";\r
2642         contents += "<span>";\r
2643         contents += "<select name=\"year\" size=\"1\">";\r
2644         for (int year = tm1.tm_year + 1900; year < tm1.tm_year + 1900 + 3; ++year)\r
2645         {\r
2646             char year_buf[64];\r
2647             sprintf_s(year_buf, "<option value=\"%d\">%d</option>", year, year);\r
2648             contents += year_buf;\r
2649         }\r
2650         contents += "</select>";\r
2651         contents += "<select name=\"month\" size=\"1\">";\r
2652         for (int month = 1; month < 13; ++month)\r
2653         {\r
2654             char month_buf[64];\r
2655             if (month == tm1.tm_mon + 1)\r
2656             {\r
2657                 sprintf_s(month_buf, "<option value=\"%d\" selected>%d</option>", month, month);\r
2658             }\r
2659             else\r
2660             {\r
2661                 sprintf_s(month_buf, "<option value=\"%d\">%d</option>", month, month);\r
2662             }\r
2663             contents += month_buf;\r
2664         }\r
2665         contents += "</select>";\r
2666         contents += "<select name=\"day\" size=\"1\">";\r
2667         for (int day = 1; day < 32; ++day)\r
2668         {\r
2669             char day_buf[64];\r
2670             if (day == tm1.tm_mday)\r
2671             {\r
2672                 sprintf_s(day_buf, "<option value=\"%d\" selected>%d</option>", day, day);\r
2673             }\r
2674             else\r
2675             {\r
2676                 sprintf_s(day_buf, "<option value=\"%d\">%d</option>", day, day);\r
2677             }\r
2678             contents += day_buf;\r
2679         }\r
2680         contents += "</select>";\r
2681         contents += "</span>";\r
2682         contents += "</div>";\r
2683         contents += "<div class=\"row\">";\r
2684         contents += "<label>";\r
2685         contents += LocalizedString(KEY_I18N_Start_Time, NULL)->cString();\r
2686         contents += " : </label>";\r
2687         contents += "<span>";\r
2688         contents += "<select name=\"start_hour\" size=\"1\">";\r
2689         for (int sh = 0; sh < 24; ++sh)\r
2690         {\r
2691             char sh_buf[64];\r
2692             if (sh == tm1.tm_hour)\r
2693             {\r
2694                 sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
2695             }\r
2696             else\r
2697             {\r
2698                 sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
2699             }\r
2700             contents += sh_buf;\r
2701         }\r
2702         contents += "</select>";\r
2703         contents += "<select name=\"start_min\" size=\"1\">";\r
2704         for (int sm = 0; sm < 60; ++sm)\r
2705         {\r
2706             char sm_buf[64];\r
2707             if (sm == tm1.tm_min)\r
2708             {\r
2709                 sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
2710             }\r
2711             else\r
2712             {\r
2713                 sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
2714             }\r
2715             contents += sm_buf;\r
2716         }\r
2717         contents += "</select>";\r
2718         contents += "</span>";\r
2719         contents += "</div>";\r
2720         contents += "<div class=\"row\">";\r
2721         contents += "<label>";\r
2722         contents += LocalizedString(KEY_I18N_End_Time, NULL)->cString();\r
2723         contents += " : </label>";\r
2724         contents += "<span>";\r
2725         contents += "<select name=\"end_hour\" size=\"1\">";\r
2726         for (int eh = 0; eh < 24; ++eh)\r
2727         {\r
2728             char eh_buf[64];\r
2729             if (eh == tm2.tm_hour)\r
2730             {\r
2731                 sprintf_s(eh_buf, "<option value=\"%02d\" selected>%02d</option>", eh, eh);\r
2732             }\r
2733             else\r
2734             {\r
2735                 sprintf_s(eh_buf, "<option value=\"%02d\">%02d</option>", eh, eh);\r
2736             }\r
2737             contents += eh_buf;\r
2738         }\r
2739         contents += "</select>";\r
2740         contents += "<select name=\"end_min\" size=\"1\">";\r
2741         for (int em = 0; em < 60; ++em)\r
2742         {\r
2743             char em_buf[64];\r
2744             if (em == tm2.tm_min)\r
2745             {\r
2746                 sprintf_s(em_buf, "<option value=\"%02d\" selected>%02d</option>", em, em);\r
2747             }\r
2748             else\r
2749             {\r
2750                 sprintf_s(em_buf, "<option value=\"%02d\">%02d</option>", em, em);\r
2751             }\r
2752             contents += em_buf;\r
2753         }\r
2754         contents += "</select>";\r
2755         contents += "</span>";\r
2756         contents += "</div>";\r
2757         contents += "<div class=\"row\">";\r
2758         contents += "<label>";\r
2759         contents += LocalizedString(KEY_I18N_Repeat, NULL)->cString();\r
2760         contents += " : </label>";\r
2761         contents += "<span>";\r
2762         contents += "<select name=\"repeat\" size=\"1\">";\r
2763         contents += "<option value=\"off\" selected>";\r
2764         contents += LocalizedString(KEY_I18N_Repeat_off, NULL)->cString();\r
2765         contents += "</option>";\r
2766         contents += "<option value=\"everyday\">";\r
2767         contents += LocalizedString(KEY_I18N_Repeat_everyday, NULL)->cString();\r
2768         contents += "</option>";\r
2769         contents += "<option value=\"weekly\">";\r
2770         contents += LocalizedString(KEY_I18N_Repeat_weekly, NULL)->cString();\r
2771         contents += "</option>";\r
2772         contents += "<option value=\"weekday\">";\r
2773         contents += LocalizedString(KEY_I18N_Repeat_weekday, NULL)->cString();\r
2774         contents += "</option>";\r
2775         contents += "</select>";\r
2776         contents += "</span>";\r
2777         contents += "</div>";\r
2778         contents += "</fieldset>";\r
2779 \r
2780         contents += "<input type=\"submit\" class=\"whiteButton\" value=\"";\r
2781         contents += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
2782         contents += "\">";\r
2783         contents += "</form>";\r
2784 \r
2785         //\r
2786         // 新規予約:ここまで\r
2787         //\r
2788 \r
2789 \r
2790         //\r
2791         // チューナ:ここから\r
2792         //\r
2793         contents += "<ul id=\"tuners\" title=\"";\r
2794         contents += LocalizedString(KEY_I18N_Tuner, NULL)->cString();\r
2795         contents += "\">";\r
2796 \r
2797         std::string controls;\r
2798         Dictionary *tunerInfos = _props->dictionaryForKey(KEY_TUNERS);\r
2799         if (tunerInfos != NULL)\r
2800         {\r
2801             for (int i = 0; i < _tunerCount; ++i)\r
2802             {\r
2803                 Dictionary *tunerInfo = tunerInfos->dictionaryForKey(_tuners[i]->name());\r
2804                 if (tunerInfo != NULL)\r
2805                 {\r
2806                     if (tunerInfo->boolForKey(KEY_INITIALIZED) && tunerInfo->boolForKey(KEY_ENABLED))\r
2807                     {\r
2808                         char key[4];\r
2809                         sprintf_s(key, "%03d", i);\r
2810 \r
2811                         // tuner list\r
2812                         contents += "<li>";\r
2813                         contents += "<a href=\"#tuner";\r
2814                         contents += key;\r
2815                         contents += "\">";\r
2816                         contents += key;\r
2817                         contents += ": ";\r
2818                         Dictionary *dict = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
2819                         if (dict != NULL)\r
2820                         {\r
2821                             char chkey[4];\r
2822                             sprintf_s(chkey, "%03d", tunerInfo->integerForKey(KEY_CHANNEL));\r
2823                             dict = dict->dictionaryForKey(chkey);\r
2824                             String *name = dict->stringForKey(KEY_NAME);\r
2825                             if (name != NULL)\r
2826                             {\r
2827                                 contents += name->cString();\r
2828                             }\r
2829                             else\r
2830                             {\r
2831                                 dict = NULL;\r
2832                             }\r
2833                         }\r
2834                         if (dict == NULL)\r
2835                         {\r
2836                             contents += _tuners[i]->name();\r
2837                         }\r
2838                         contents += "</a></li>";\r
2839 \r
2840                         // controls\r
2841                         controls += "<form name=\"control\" id=\"tuner";\r
2842                         controls += key;\r
2843                         controls += "\" class=\"panel\" method=\"GET\" action=\"/";\r
2844                         controls += key;\r
2845                         controls += "/recording=on\" target=\"_self\">";\r
2846 \r
2847                         // 白い枠:ここから\r
2848                         controls += "<fieldset>";\r
2849 \r
2850                         // 局指定:ここから\r
2851                         controls += "<div class=\"row\">";\r
2852                         controls += "<label>";\r
2853                         controls += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
2854                         controls += " : </label>";\r
2855                         controls += "<span>";\r
2856                         controls += "<select name=\"channel\" size=\"1\">";\r
2857                         Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
2858                         if (channels != NULL)\r
2859                         {\r
2860                             for (int ch = 0; ch <= Tuner::MAX_CHANNELS_ISDB_T; ++ch)\r
2861                             {\r
2862                                 char chkey[4];\r
2863                                 sprintf_s(chkey, "%03d", ch);\r
2864                                 dict = channels->dictionaryForKey(chkey);\r
2865                                 if (dict == NULL)\r
2866                                 {\r
2867                                     break;\r
2868                                 }\r
2869                                 Array *services = (Array *)dict->objectForKey(KEY_SERVICES);\r
2870                                 if (services != NULL)\r
2871                                 {\r
2872                                     for (uint s = 0; s < services->count(); ++s)\r
2873                                     {\r
2874                                         Dictionary *service = (Dictionary *)services->objectAtIndex(s);\r
2875                                         if (service != NULL)\r
2876                                         {\r
2877                                             char sid[8];\r
2878                                             sprintf_s(sid, "%d", service->integerForKey(KEY_SERVICE_ID));\r
2879                                             controls += "<option value=\"";\r
2880                                             controls += chkey;\r
2881                                             controls += "\"";\r
2882                                             // selected\r
2883                                             controls += ">";\r
2884                                             String *name = service->stringForKey(KEY_NAME);\r
2885                                             if (name != NULL)\r
2886                                             {\r
2887                                                 controls += name->cString();\r
2888                                             }\r
2889                                             controls += "[";\r
2890                                             controls += sid;\r
2891                                             controls += "]</option>";\r
2892                                         }\r
2893                                     }\r
2894                                 }\r
2895                             }\r
2896                         }\r
2897                         controls += "</select>";\r
2898                         controls += "</span>";\r
2899                         controls += "</div>";\r
2900                         // 局指定:ここまで\r
2901 \r
2902                         // 時間指定:ここから\r
2903                         controls += "<div class=\"row\">";\r
2904                         controls += "<label>";\r
2905                         controls += LocalizedString(KEY_I18N_Time, NULL)->cString();\r
2906                         controls += " : </label>";\r
2907                         controls += "<span>";\r
2908                         controls += "<select name=\"hour\" size=\"1\">";\r
2909                         for (int sh = 0; sh < 24; ++sh)\r
2910                         {\r
2911                             char sh_buf[64];\r
2912                             if (sh == 1)\r
2913                             {\r
2914                                 sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
2915                             }\r
2916                             else\r
2917                             {\r
2918                                 sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
2919                             }\r
2920                             controls += sh_buf;\r
2921                         }\r
2922                         controls += "</select>";\r
2923                         controls += "<select name=\"min\" size=\"1\">";\r
2924                         for (int sm = 0; sm < 60; ++sm)\r
2925                         {\r
2926                             char sm_buf[64];\r
2927                             if (sm == 0)\r
2928                             {\r
2929                                 sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
2930                             }\r
2931                             else\r
2932                             {\r
2933                                 sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
2934                             }\r
2935                             controls += sm_buf;\r
2936                         }\r
2937                         controls += "</select>";\r
2938                         controls += "</span>";\r
2939                         controls += "</div>";\r
2940                         // 時間指定:ここまで\r
2941 \r
2942                         controls += "</fieldset>";\r
2943                         // 白い枠:ここまで\r
2944 \r
2945                         controls += "<input type=\"submit\" class=\"whiteButton\" value=\"";\r
2946                         controls += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
2947                         controls += "\">";\r
2948                         controls += "</form>";\r
2949                     }\r
2950                 }\r
2951             }\r
2952         }\r
2953         contents += "</ul>";\r
2954         contents += controls;\r
2955 \r
2956         //\r
2957         // チューナ:ここまで\r
2958         //\r
2959 \r
2960         //\r
2961         // キーワード:ここから\r
2962         //\r
2963         contents += "<ul id=\"keywords\" title=\"";\r
2964         contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
2965         contents += "\">";\r
2966 \r
2967         contents += "<li><a href=\"#dialogForm\">";\r
2968         contents += "<font color=\"red\">[";\r
2969 //        contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
2970         contents += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
2971         contents += "]</font>";\r
2972         contents += "</a></li>";\r
2973 \r
2974         // 登録済みキーワード\r
2975         Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
2976         if (keywords_info != NULL)\r
2977         {\r
2978             controls = "";\r
2979             Array *keys = keywords_info->allKeys();\r
2980             if (keys != NULL)\r
2981             {\r
2982                 for (uint i = 0; i < keys->count(); ++i)\r
2983                 {\r
2984                     Dictionary *kwd_inf = keywords_info->dictionaryForKey((String *)keys->objectAtIndex(i));\r
2985                     if (kwd_inf == NULL)\r
2986                     {\r
2987                         continue;\r
2988                     }\r
2989 \r
2990                     char tmp[32];\r
2991                     sprintf_s(tmp, "%d", i);\r
2992                     contents += "<li><a href=\"#keywords_";\r
2993                     contents += tmp;\r
2994                     contents += "\">";\r
2995                     contents += ((String *)keys->objectAtIndex(i))->cString();\r
2996                     contents += "</a></li>";\r
2997 \r
2998                     controls += "<form name=\"filter\" id=\"keywords_";\r
2999                     controls += tmp;\r
3000                     controls += "\" class=\"panel\" method=\"GET\" action=\"mod_keywords.cgi\" target=\"_self\">";\r
3001                     controls += "<input type=\"hidden\" name=\"keywords\" value=\"";\r
3002                     controls += ((String *)keys->objectAtIndex(i))->cString();\r
3003                     controls += "\">";\r
3004                     controls += "<fieldset>";\r
3005 \r
3006                     controls += "<div class=\"row\">";\r
3007                     controls += "<label>";\r
3008                     controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
3009                     controls += " : </label>";\r
3010                     controls += "<span>";\r
3011                     controls += ((String *)keys->objectAtIndex(i))->cString();\r
3012                     controls += "</span>";\r
3013                     controls += "</div>";\r
3014 \r
3015                     controls += "<div class=\"row\">";\r
3016                     controls += "<label>";\r
3017                     controls += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
3018                     controls += " : </label>";\r
3019                     controls += "<span>";\r
3020                     controls += "<select name=\"service_id\" size=\"1\">";\r
3021                     controls += "<option value=\"-\">----</option>";\r
3022 \r
3023                     String *service_id = kwd_inf->stringForKey(KEY_EPG_SERVICE_ID);\r
3024 \r
3025                     // 局情報の収集\r
3026                     Array *stationInfos = Controller::stationInfos(Tuner::ISDB_T);\r
3027                     if (stationInfos != NULL)\r
3028                     {\r
3029                         stationInfos->addObjectsFromArray(Controller::stationInfos(Tuner::ISDB_S));\r
3030                     }\r
3031                     else\r
3032                     {\r
3033                         stationInfos = Controller::stationInfos(Tuner::ISDB_S);\r
3034                     }\r
3035                     for (uint i = 0; (stationInfos != NULL) && (i < stationInfos->count()); ++i)\r
3036                     {\r
3037                         Array *services = ((Dictionary *)stationInfos->objectAtIndex(i))->arrayForKey(KEY_SERVICES);\r
3038 \r
3039                         for (uint j = 0; (services != NULL) &&  (j < 1); ++j)\r
3040                         {\r
3041                             Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
3042 \r
3043                             // テレビ放送かチェック\r
3044                             if ((service->stringForKey(KEY_SERVICE_TYPE) != NULL) && service->stringForKey(KEY_SERVICE_TYPE)->isEqualToString("1"))\r
3045                             {\r
3046                                 String *name = service->stringForKey(KEY_NAME);\r
3047                                 String *sid  = service->stringForKey(KEY_SERVICE_ID);\r
3048                                 if ((name != NULL) && (sid != NULL))\r
3049                                 {\r
3050                                     if ((service_id != NULL) && (service_id->isEqualToString(sid)))\r
3051                                     {\r
3052                                         String *tmp = String::stringWithFormat("<option value=\"%s\" selected>%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
3053                                         controls += tmp->cString();\r
3054                                     }\r
3055                                     else\r
3056                                     {\r
3057                                         String *tmp = String::stringWithFormat("<option value=\"%s\">%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
3058                                         controls += tmp->cString();\r
3059                                     }\r
3060                                 }\r
3061                             }\r
3062                         }\r
3063                     }\r
3064 \r
3065                     controls += "</select>";\r
3066                     controls += "</span>";\r
3067                     controls += "</div>";\r
3068 \r
3069                     controls += "<div class=\"row\">";\r
3070                     controls += "<label>";\r
3071                     controls += LocalizedString(KEY_I18N_Start_Time, NULL)->cString();\r
3072                     controls += " : </label>";\r
3073                     controls += "<span>";\r
3074                     controls += "<select name=\"start_hour\" size=\"1\">";\r
3075                     controls += "<option value=\"-\">--</option>";\r
3076                     String *sh_str = kwd_inf->stringForKey(KEY_EPG_START);\r
3077                     for (int sh = 0; sh < 24; ++sh)\r
3078                     {\r
3079                         char sh_buf[64];\r
3080                         if ((sh_str != NULL) && (sh_str->length() == 5) && (sh_str->substringToIndex(2)->intValue() == sh))\r
3081                         {\r
3082                             sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
3083                         }\r
3084                         else\r
3085                         {\r
3086                             sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
3087                         }\r
3088                         controls += sh_buf;\r
3089                     }\r
3090                     controls += "</select>";\r
3091                     controls += "<select name=\"start_min\" size=\"1\">";\r
3092                     controls += "<option value=\"-\">--</option>";\r
3093                     for (int sm = 0; sm < 60; ++sm)\r
3094                     {\r
3095                         char sm_buf[64];\r
3096                         if ((sh_str != NULL) && (sh_str->length() == 5) && (sh_str->substringFromIndex(3)->intValue() == sm))\r
3097                         {\r
3098                             sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
3099                         }\r
3100                         else\r
3101                         {\r
3102                             sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
3103                         }\r
3104                         controls += sm_buf;\r
3105                     }\r
3106                     controls += "</select>";\r
3107                     controls += "</span>";\r
3108                     controls += "</div>";\r
3109 \r
3110                     controls += "</fieldset>";\r
3111 \r
3112                     controls += "<input type=\"submit\" class=\"whiteButton\" name=\"req_mod\" value=\"";\r
3113                     controls += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
3114                     controls += "\">";\r
3115 \r
3116                     controls += "<input type=\"submit\" class=\"redButton\" name=\"req_del\" value=\"";\r
3117                     controls += LocalizedString(KEY_I18N_Delete, NULL)->cString();\r
3118 //                    controls += "\">";\r
3119                     controls += "\" onclick='return confirm(\"To delete. Is it ok?\");'>";\r
3120 \r
3121                     controls += "</form>";\r
3122                 }\r
3123             }\r
3124         }\r
3125 \r
3126         contents += "</ul>";\r
3127         contents += controls;\r
3128 \r
3129         controls = "<form id=\"dialogForm\" title=\"";\r
3130         controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
3131         controls += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
3132         controls += "\" class=\"dialog\" target=\"_self\" action=\"add_keywords.cgi\" method=\"GET\">";\r
3133         controls += "<fieldset>";\r
3134         controls += "<h1>";\r
3135         controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
3136         controls += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
3137         controls += "</h1>";\r
3138         controls += "<a class=\"button leftButton\" type=\"cancel\">Cancel</a>";\r
3139         controls += "<a class=\"button blueButton\" type=\"submit\">Submit</a>";\r
3140         controls += "<label>Parm1:</label>";\r
3141         controls += "<input type=\"text\" name=\"keywords\" value=\"";\r
3142 //        controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
3143         controls += "\"/>";\r
3144         controls += "</fieldset>";\r
3145         controls += "<div class=\"spinner\"></div>";\r
3146         controls += "</form>";\r
3147 \r
3148         contents += controls;\r
3149         //\r
3150         // キーワード:ここまで\r
3151         //\r
3152 \r
3153         html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
3154         if (html != NULL)\r
3155         {\r
3156             result = responseWithHTML(request, html);\r
3157         }\r
3158 \r
3159         break;\r
3160     }\r
3161 \r
3162     // unlock\r
3163     RaymUnlock(this);\r
3164 \r
3165     return result;\r
3166 }\r
3167 \r
3168 HTTPResponse *Controller::responseForStatus(HTTPRequest *request, SOCKADDR_IN *client)\r
3169 {\r
3170     HTTPResponse *result = NULL;\r
3171     if ((request != NULL) && (client != NULL))\r
3172     {\r
3173         String *path = _httpd->rootPath()->stringByAppendingPathComponent("template2.html");\r
3174         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
3175         if (html != NULL)\r
3176         {\r
3177             html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Tuner_Status, NULL));\r
3178             std::string contents;\r
3179             std::string status;\r
3180             contents += "<ul id=\"home\" title=\"";\r
3181             contents += LocalizedString(KEY_I18N_Tuner_Status, NULL)->cString();\r
3182             contents += "\" selected=\"true\">";\r
3183             DebugLog2("_tunerCount = %d", _tunerCount);\r
3184             for (int i = 0; i < _tunerCount; ++i)\r
3185             {\r
3186                 char key[4];\r
3187                 sprintf_s(key, sizeof(key), "%03d", i);\r
3188 \r
3189                 if (isTunerInitialized(i))\r
3190                 {\r
3191                     contents += "<li>";\r
3192                     contents += "<a href=\"#tuner";\r
3193                     contents += key;\r
3194                     contents += "\">";\r
3195                     contents += key;\r
3196                     contents += ": ";\r
3197                     contents += _tuners[i]->name();\r
3198                     contents += "</a></li>";\r
3199 \r
3200                     //\r
3201                     status += "<div id=\"";\r
3202                     status += "tuner";\r
3203                     status += key;\r
3204                     status += "\" title=\"";\r
3205                     status += _tuners[i]->name();\r
3206                     status += "\" class=\"panel\">";\r
3207 \r
3208                     status += "<h2>Status</h2>";\r
3209                     status += "<fieldset>";\r
3210 \r
3211                     // Tuner Type\r
3212                     status += "<div class=\"row\">";\r
3213                     status += "<label>Type</label>";\r
3214                     status += "<span>";\r
3215                     switch (_tuners[i]->type())\r
3216                     {\r
3217                     case Tuner::ISDB_S:\r
3218                         status += "ISDB-S";\r
3219                         break;\r
3220                     case Tuner::ISDB_T:\r
3221                         status += "ISDB-T";\r
3222                         break;\r
3223                     case Tuner::TYPE_NA:\r
3224                     default:\r
3225                         status += "N/A";\r
3226                         break;\r
3227                     }\r
3228                     status += "</span>";\r
3229                     status += "</div>";\r
3230 \r
3231                     // LnbPower\r
3232                     status += "<div class=\"row\">";\r
3233                     status += "<label>LnbPower</label>";\r
3234                     status += "<span>";\r
3235                     switch (_tuners[i]->lnbPower())\r
3236                     {\r
3237                     case Tuner::LNB_POWER_11V:\r
3238                         status += "11V";\r
3239                         break;\r
3240                     case Tuner::LNB_POWER_15V:\r
3241                         status += "15V";\r
3242                         break;\r
3243                     case Tuner::LNB_POWER_OFF:\r
3244                     default:\r
3245                         status += "OFF";\r
3246                         break;\r
3247                     }\r
3248                     status += "</span>";\r
3249                     status += "</div>";\r
3250 \r
3251                     // Channel\r
3252                     char tmpstr[32];\r
3253                     status += "<div class=\"row\">";\r
3254                     status += "<label>Channel</label>";\r
3255                     status += "<span>";\r
3256                     sprintf_s(tmpstr, sizeof(tmpstr), "%03d", _tuners[i]->channel());\r
3257                     status += tmpstr;\r
3258                     status += "</span>";\r
3259                     status += "</div>";\r
3260 \r
3261                     // C/N[dB] AGC xxx/255\r
3262                     uint32_t cn100 = 0;\r
3263                     uint32_t agc = 0;\r
3264                     uint32_t maxAgc = 0;\r
3265                     _tuners[i]->getCnAgc(&cn100, &agc, &maxAgc);\r
3266 \r
3267                     sprintf_s(tmpstr, sizeof(tmpstr), "%d.%02d[dB]", cn100 / 100, cn100 % 100);\r
3268 \r
3269                     status += "<div class=\"row\">";\r
3270                     status += "<label>C/N</label>";\r
3271                     status += "<span>";\r
3272                     status += tmpstr;\r
3273                     status += "</span>";\r
3274                     status += "</div>";\r
3275 \r
3276                     sprintf_s(tmpstr, sizeof(tmpstr), "%03d/%03d", agc, maxAgc);\r
3277 \r
3278                     status += "<div class=\"row\">";\r
3279                     status += "<label>AGC</label>";\r
3280                     status += "<span>";\r
3281                     status += tmpstr;\r
3282                     status += "</span>";\r
3283                     status += "</div>";\r
3284 \r
3285                     status += "</fieldset>";\r
3286                     status += "</div>";\r
3287 \r
3288                 }\r
3289                 else\r
3290                 {\r
3291                     contents += "<li>";\r
3292                     contents += _tuners[i]->name();\r
3293                     contents += "[uninitialized]";\r
3294                     contents += "</li>";\r
3295                 }\r
3296             }\r
3297             contents += "</ul>";\r
3298             contents += status;\r
3299             html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
3300             result = responseWithHTML(request, html);\r
3301         }\r
3302         else\r
3303         {\r
3304             DebugLog2("responseForStatus() html is null\n");\r
3305         }\r
3306     }\r
3307     return result;\r
3308 }\r
3309 \r
3310 HTTPResponse *Controller::responseForRegistCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
3311 {\r
3312     DebugLog2("Controller::responseForRegistCGI()");\r
3313 \r
3314     HTTPResponse *result = NULL;\r
3315 \r
3316     // CGIリクエストとして解析\r
3317     Dictionary *cgi = request->parseAsCGI();\r
3318     if (cgi != NULL)\r
3319     {\r
3320         // CGIパスが一致しているか\r
3321         if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/regist.cgi")))\r
3322         {\r
3323             // パラメータがあるか\r
3324             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
3325             if (params != NULL)\r
3326             {\r
3327                 // パラメータ数が2か\r
3328                 if (params->count() == 2)\r
3329                 {\r
3330                     // パラメータのチェック\r
3331                     String *service_id = NULL;\r
3332                     String *event_id = NULL;\r
3333 \r
3334                     for (uint i = 0; i < params->count(); ++i)\r
3335                     {\r
3336                         Dictionary *param = (Dictionary *)params->objectAtIndex(i);\r
3337                         String *value = param->stringForKey("service_id");\r
3338                         if ((value != NULL) && value->isMatch("^\\d+$"))\r
3339                         {\r
3340                             service_id = value;\r
3341                         }\r
3342                         value = param->stringForKey("event_id");\r
3343                         if ((value != NULL) && value->isMatch("^\\d+$"))\r
3344                         {\r
3345                             event_id = value;\r
3346                         }\r
3347                     }\r
3348 \r
3349                     // 有効なパラメータか\r
3350                     if ((service_id != NULL) && (event_id != NULL))\r
3351                     {\r
3352                         DebugLog2("valid request");\r
3353 \r
3354                         result = responseByResultAndReferer(request, reserve(service_id->intValue(), event_id->intValue()), URI_PROGRAMS_HTML);\r
3355                     }\r
3356                 }\r
3357 \r
3358                 // パラメータ数が9か\r
3359                 else if (params->count() == 9)\r
3360                 {\r
3361                     // パラメータのチェック\r
3362                     String *service_id = NULL;\r
3363                     String *year       = NULL;\r
3364                     String *month      = NULL;\r
3365                     String *day        = NULL;\r
3366                     String *start_hour = NULL;\r
3367                     String *start_min  = NULL;\r
3368                     String *end_hour   = NULL;\r
3369                     String *end_min    = NULL;\r
3370                     String *repeat     = NULL;\r
3371 \r
3372                     struct {\r
3373                         const char *name;\r
3374                         String **variable;\r
3375                         const char *regex;\r
3376                     }\r
3377                     cgi[] =\r
3378                     {\r
3379                         {"service_id", &service_id, "^\\d+$"},\r
3380                         {"year",       &year,       "^\\d{4}$"},\r
3381                         {"month",      &month,      "^([1-9]|1[0-2])$"},\r
3382                         {"day",        &day,        "^([1-9]|[12][0-9]|3[01])$"},\r
3383                         {"start_hour", &start_hour, "^\\d{2}$"},\r
3384                         {"start_min",  &start_min,  "^\\d{2}$"},\r
3385                         {"end_hour",   &end_hour,   "^\\d{2}$"},\r
3386                         {"end_min",    &end_min,    "^\\d{2}$"},\r
3387                         {"repeat",     &repeat,     "^(off|everyday|weekly|weekday)$"},\r
3388                         {NULL, NULL, NULL}\r
3389                     };\r
3390 \r
3391                     for (uint i = 0; cgi[i].name != NULL; ++i)\r
3392                     {\r
3393                         for (uint j = 0; j < params->count(); ++j)\r
3394                         {\r
3395                             Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
3396                             String *value = param->stringForKey(cgi[i].name);\r
3397                             if ((value != NULL) && value->isMatch(cgi[i].regex))\r
3398                             {\r
3399                                 *(cgi[i].variable) = value;\r
3400                                 break;\r
3401                             }\r
3402                         }\r
3403                     }\r
3404 \r
3405                     // 有効なパラメータか\r
3406                     if ((service_id != NULL) && (year != NULL) && (month != NULL) && (day != NULL) &&\r
3407                         (start_hour != NULL) && (start_min != NULL) && (end_hour != NULL) && (end_min != NULL) && (repeat != NULL))\r
3408                     {\r
3409                         //\r
3410                         DebugLog1("valid param");\r
3411 \r
3412                         Dictionary *epg = Dictionary::dictionaryWithCapacity(0);\r
3413 \r
3414                         // 日付\r
3415                         epg->setString(String::stringWithFormat("%s/%02d/%02d", year->cString(), month->intValue(), day->intValue()), KEY_EPG_DATE);\r
3416 \r
3417                         // 開始時刻\r
3418                         epg->setString(String::stringWithFormat("%s:%s:00", start_hour->cString(), start_min->cString()), KEY_EPG_START);\r
3419 \r
3420                         // 終了時刻\r
3421                         epg->setString(String::stringWithFormat("%s:%s:00", end_hour->cString(), end_min->cString()), KEY_EPG_END);\r
3422 \r
3423                         // 繰り返し\r
3424                         epg->setString(repeat, KEY_EPG_REPEAT);\r
3425 \r
3426                         // Service ID\r
3427                         epg->setString(service_id, KEY_EPG_SERVICE_ID);\r
3428 \r
3429                         // Status\r
3430                         epg->setString("ready", KEY_EPG_STATUS);\r
3431 \r
3432                         result = responseByResultAndReferer(request, reserve(epg), URI_RESERVATION_HTML);\r
3433                     }\r
3434                 }\r
3435             }\r
3436         }\r
3437     }\r
3438 \r
3439     return result;\r
3440 }\r
3441 \r
3442 HTTPResponse *Controller::responseForCancelCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
3443 {\r
3444     DebugLog2("Controller::responseForCancelCGI()");\r
3445 \r
3446     HTTPResponse *result = NULL;\r
3447 \r
3448     // CGIリクエストとして解析\r
3449     Dictionary *cgi = request->parseAsCGI();\r
3450     if (cgi != NULL)\r
3451     {\r
3452         // CGIパスが一致しているか\r
3453         if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/cancel.cgi")))\r
3454         {\r
3455             // パラメータがあるか\r
3456             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
3457             if (params != NULL)\r
3458             {\r
3459                 // パラメータ数が1か\r
3460                 if (params->count() == 1)\r
3461                 {\r
3462                     Dictionary *param = (Dictionary *)params->objectAtIndex(0);\r
3463                     String *value = param->stringForKey("resv_id");\r
3464                     if ((value != NULL) && value->isMatch("^\\d{6}$"))\r
3465                     {\r
3466                         result = responseByResultAndReferer(request, cancel(-1, value->intValue()), URI_RESERVATION_HTML);\r
3467                     }\r
3468                 }\r
3469             }\r
3470         }\r
3471     }\r
3472     return result;\r
3473 }\r
3474 \r
3475 HTTPResponse *Controller::responseForAddKeywordsCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
3476 {\r
3477     DebugLog2("Controller::responseForAddKeywordsCGI()");\r
3478 \r
3479     HTTPResponse *result = NULL;\r
3480 \r
3481     // CGIリクエストとして解析\r
3482     Dictionary *cgi = request->parseAsCGI();\r
3483     if (cgi != NULL)\r
3484     {\r
3485         // CGIパスが一致しているか\r
3486         if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/add_keywords.cgi")))\r
3487         {\r
3488             // パラメータがあるか\r
3489             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
3490             if (params != NULL)\r
3491             {\r
3492                 // パラメータ数が1か\r
3493                 if (params->count() == 1)\r
3494                 {\r
3495                     Dictionary *param = (Dictionary *)params->objectAtIndex(0);\r
3496                     String *value = param->stringForKey("keywords");\r
3497                     if (value != NULL)\r
3498                     {\r
3499                         value = value->stringByReplacingOccurrencesOfString("+", " ");\r
3500                         if (value != NULL)\r
3501                         {\r
3502                             value = value->stringByRemovingPercentEncoding();\r
3503                         }\r
3504                     }\r
3505                     if (value != NULL)\r
3506                     {\r
3507                         // lock\r
3508                         RaymLock(this);\r
3509 \r
3510                         Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
3511                         if (keywords_info == NULL)\r
3512                         {\r
3513                             keywords_info = Dictionary::dictionaryWithCapacity(0);\r
3514                             _reservations->setObject(keywords_info, KEY_EPG_KEYWORDS);\r
3515                         }\r
3516 \r
3517                         if (keywords_info->dictionaryForKey(value) == NULL)\r
3518                         {\r
3519                             // 新規キーワードの場合\r
3520                             keywords_info->setObject(Dictionary::dictionaryWithCapacity(0), value);\r
3521 \r
3522                             // キーワード予約更新\r
3523                             updateKeywordsReservation();\r
3524 \r
3525                             // スケジュール更新\r
3526                             updateSchedule();\r
3527 \r
3528                             // 保存\r
3529                             _reservations->writeToFile(_reservations_path, true);\r
3530                         }\r
3531 \r
3532                         RaymUnlock(this);\r
3533 \r
3534                         result = responseForReloadURI(request, client, "/reservation.html#_keywords");\r
3535                     }\r
3536                 }\r
3537             }\r
3538         }\r
3539     }\r
3540     return result;\r
3541 }\r
3542 \r
3543 HTTPResponse *Controller::responseForModKeywordsCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
3544 {\r
3545     DebugLog2("Controller::responseForModKeywordsCGI()");\r
3546 \r
3547     HTTPResponse *result = NULL;\r
3548 \r
3549     // CGIリクエストとして解析\r
3550     Dictionary *cgi = request->parseAsCGI();\r
3551     if (cgi != NULL)\r
3552     {\r
3553         DebugLog3("cgi != NULL");\r
3554 \r
3555         // CGIパスが一致しているか\r
3556         if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/mod_keywords.cgi")))\r
3557         {\r
3558             DebugLog3("CGI path OK.");\r
3559 \r
3560             // パラメータがあるか\r
3561             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
3562             if (params != NULL)\r
3563             {\r
3564                 DebugLog3("params != NULL");\r
3565 \r
3566                 // パラメータのチェック\r
3567                 String *keywords   = NULL;\r
3568                 String *service_id = NULL;\r
3569                 String *start_hour = NULL;\r
3570                 String *start_min  = NULL;\r
3571                 String *req_mod    = NULL;\r
3572                 String *req_del    = NULL;\r
3573 \r
3574                 struct {\r
3575                     const char *name;\r
3576                     String **variable;\r
3577                     const char *regex;\r
3578                 }\r
3579                 cgi[] =\r
3580                 {\r
3581                     {"keywords",   &keywords,   ".*"},\r
3582                     {"service_id", &service_id, "^(\\d+|-)$"},\r
3583                     {"start_hour", &start_hour, "^(\\d{2}|-)$"},\r
3584                     {"start_min",  &start_min,  "^(\\d{2}|-)$"},\r
3585                     {"req_mod",    &req_mod,    ".*"},\r
3586                     {"req_del",    &req_del,    ".*"},\r
3587                     {NULL, NULL, NULL}\r
3588                 };\r
3589 \r
3590                 for (uint i = 0; cgi[i].name != NULL; ++i)\r
3591                 {\r
3592                     for (uint j = 0; j < params->count(); ++j)\r
3593                     {\r
3594                         Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
3595                         String *value = param->stringForKey(cgi[i].name);\r
3596                         if ((value != NULL) && value->isMatch(cgi[i].regex))\r
3597                         {\r
3598                             *(cgi[i].variable) = value;\r
3599                             break;\r
3600                         }\r
3601                     }\r
3602                 }\r
3603 \r
3604                 if (keywords != NULL)\r
3605                 {\r
3606                     keywords = keywords->stringByReplacingOccurrencesOfString("+", " ");\r
3607                     if (keywords != NULL)\r
3608                     {\r
3609                         keywords = keywords->stringByRemovingPercentEncoding();\r
3610                     }\r
3611                 }\r
3612 \r
3613                 if ((keywords != NULL) && (service_id != NULL) && (start_hour != NULL) && (start_min != NULL) &&\r
3614                     ((req_mod != NULL) && (req_del == NULL)) || ((req_mod == NULL) && (req_del != NULL)))\r
3615                 {\r
3616 \r
3617                     // lock\r
3618                     RaymLock(this);\r
3619 \r
3620                     Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
3621                     if (keywords_info != NULL)\r
3622                     {\r
3623                         if (req_mod != NULL)\r
3624                         {\r
3625                             DebugLog0("keywords: %s", keywords->cString());\r
3626                             DebugLog0("service_id: %s", service_id->cString());\r
3627                             DebugLog0("start_hour: %s", start_hour->cString());\r
3628                             DebugLog0("start_min: %s", start_min->cString());\r
3629                             Dictionary *kwd_inf = keywords_info->dictionaryForKey(keywords);\r
3630                             if (kwd_inf != NULL)\r
3631                             {\r
3632                                 if (!service_id->isEqualToString("-"))\r
3633                                 {\r
3634                                     kwd_inf->setString(service_id, KEY_EPG_SERVICE_ID);\r
3635                                 }\r
3636                                 else\r
3637                                 {\r
3638                                     kwd_inf->removeObjectForKey(KEY_EPG_SERVICE_ID);\r
3639                                 }\r
3640                                 if (!start_hour->isEqualToString("-") && !start_min->isEqualToString("-"))\r
3641                                 {\r
3642                                     kwd_inf->setString(String::stringWithFormat("%s:%s", start_hour->cString(), start_min->cString()), KEY_EPG_START);\r
3643                                 }\r
3644                                 else\r
3645                                 {\r
3646                                     kwd_inf->removeObjectForKey(KEY_EPG_START);\r
3647                                 }\r
3648                             }\r
3649                         }\r
3650                         else if (req_del != NULL)\r
3651                         {\r
3652                             DebugLog0("keywords: %s", keywords->cString());\r
3653                             keywords_info->removeObjectForKey(keywords);\r
3654                         }\r
3655 \r
3656                         // キーワード予約更新\r
3657                         updateKeywordsReservation();\r
3658 \r
3659                         // スケジュール更新\r
3660                         updateSchedule();\r
3661 \r
3662                         // 保存\r
3663                         _reservations->writeToFile(_reservations_path, true);\r
3664                     }\r
3665 \r
3666                     // unlock\r
3667                     RaymUnlock(this);\r
3668 \r
3669                     result = responseForReloadURI(request, client, "/reservation.html#_keywords");\r
3670                 }\r
3671             }\r
3672         }\r
3673     }\r
3674     return result;\r
3675 }\r
3676 \r
3677 HTTPResponse *Controller::responseForReloadURI(NET::HTTPRequest *request, SOCKADDR_IN *client, const char *uri, int sec)\r
3678 {\r
3679     HTTPResponse *result = NULL;\r
3680 \r
3681     if (uri != NULL)\r
3682     {\r
3683         std::string contents;\r
3684         contents = "<html>";\r
3685         contents += "<head>";\r
3686 #if 0\r
3687         contents += "<meta http-equiv=\"refresh\" content=\"0;URL=";\r
3688 #else\r
3689         contents += "<meta http-equiv=\"refresh\" content=\"";\r
3690         contents += sec;\r
3691         contents += ";URL=";\r
3692 #endif\r
3693         contents += uri;\r
3694         contents += "\">";\r
3695         contents += "</head>";\r
3696         contents += "</html>";\r
3697         String *html = String::stringWithUTF8String(contents.c_str());\r
3698         if (html != NULL)\r
3699         {\r
3700             result = responseWithHTML(request, html);\r
3701         }\r
3702     }\r
3703 \r
3704     return result;\r
3705 }\r
3706 \r
3707 HTTPResponse *Controller::responseForPlaylist(HTTPRequest *request, SOCKADDR_IN *client)\r
3708 {\r
3709     HTTPResponse *result = NULL;\r
3710     // http://bit.ly/iptv_feb2015\r
3711 \r
3712 #if 0\r
3713 \r
3714 #EXTM3U\r
3715 \r
3716 #EXTINF:-1, [COLOR yellow]Updated 15/04/2015 @ 03:45 [/COLOR]  \r
3717 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
3718 #EXTINF:-1, [COLOR green] --Uk Live Tv--[/COLOR]  \r
3719 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
3720 #EXTINF:-1, [COLOR green] --Free Collection of IPTV sports links--[/COLOR]  \r
3721 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
3722 #EXTINF:-1, [COLOR red] --Links will go down. Please note that it will take time to update--[/COLOR]\r
3723 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
3724 #EXTINF:-1, [COLOR red] --Please contact me at the husham.com website--[/COLOR]\r
3725 plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
3726 \r
3727 #EXTINF:-1, Sky sports news\r
3728 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
3729 #EXTINF:-1,Bt sports 1\r
3730 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
3731 \r
3732 #endif\r
3733 \r
3734 \r
3735     std::string contents;\r
3736     contents = "#EXTM3U\r\n";\r
3737     contents += "\r\n";\r
3738 \r
3739     Dictionary *tuner_channel_to_udp = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
3740     if (tuner_channel_to_udp != NULL)\r
3741     {\r
3742         Tuner::Type types[] = {Tuner::ISDB_T, Tuner::ISDB_S};\r
3743         for (int t = 0; t < sizeof(types) / sizeof(Tuner::Type); ++t)\r
3744         {\r
3745             for (int i = 0; i < _tunerCount; ++i)\r
3746             {\r
3747                 if (isTunerInitialized(i) && (_tuners[i]->type() == types[t]))\r
3748                 {\r
3749                     uint ch_max = ((types[t] == Tuner::ISDB_T) ? Tuner::MAX_CHANNELS_ISDB_T : Tuner::MAX_CHANNELS_ISDB_S);\r
3750                     for (uint ch = 0; ch <= ch_max; ++ch)\r
3751                     {\r
3752                         if (isChannelEnabled(i, ch))\r
3753                         {\r
3754                             char tuner_and_channel[10];\r
3755                             sprintf_s(tuner_and_channel, "%d,%d", i, ch);\r
3756                             char *chstr = strchr(tuner_and_channel, ',');\r
3757                             if (chstr != NULL)\r
3758                             {\r
3759                                 ++chstr;\r
3760                             }\r
3761 \r
3762                             String *udp_str = tuner_channel_to_udp->stringForKey(tuner_and_channel);\r
3763                             if (udp_str != NULL)\r
3764                             {\r
3765                                 contents += "#EXTINF:-1 tvg-id=\"";\r
3766                                 contents += tuner_and_channel;\r
3767                                 contents += "\" tvg-logo=\"";\r
3768                                 contents += ((types[t] == Tuner::ISDB_T) ? "t_" : "s_");\r
3769                                 if (chstr != NULL)\r
3770                                 {\r
3771                                     contents += chstr;\r
3772                                 }\r
3773                                 else\r
3774                                 {\r
3775                                     contents += "ffff";\r
3776                                 }\r
3777                                 contents += "\" group-title=\"";\r
3778                                 contents += _tuners[i]->name();\r
3779                                 contents += "\", ";\r
3780                                 String *station_name = stationName(types[t], ch);\r
3781                                 if (station_name != NULL)\r
3782                                 {\r
3783                                     contents += station_name->cString();\r
3784                                 }\r
3785                                 else\r
3786                                 {\r
3787                                     contents += tuner_and_channel;\r
3788                                 }\r
3789                                 contents += "\r\n";\r
3790                                 contents += "udp://0.0.0.0:";\r
3791                                 contents += udp_str->cString();\r
3792                                 contents += "\r\n";\r
3793                             }\r
3794                         }\r
3795                     }\r
3796                 }\r
3797             }\r
3798         }\r
3799 \r
3800     }\r
3801 \r
3802     String *text = String::stringWithUTF8String(contents.c_str());\r
3803     if (text != NULL)\r
3804     {\r
3805         result = responseWithUTF8Text(request, text);\r
3806     }\r
3807 \r
3808     return result;\r
3809 }\r
3810 \r
3811 /**\r
3812  * @brief HTTP Live Streaming制御\r
3813  *\r
3814  *     http://hogehoge/チューナ番号/チャンネル番号/streaming[-プリセット名].m3u8 がリクエスト(プリセットはオプション)され、\r
3815  *     チューナ番号/チャンネル番号/プリセット名(ある場合)が有効値の場合にコールされる\r
3816  *\r
3817  * @param [in] request    HTTPリクエスト\r
3818  * @param [in] client     リクエストしたクライアントのアドレス\r
3819  * @param [in] tuner      チューナ番号\r
3820  * @param [in] channel    チャンネル番号\r
3821  * @param [in] preset     プリセット(URLで省略された場合 "default" )\r
3822  */\r
3823 HTTPResponse *Controller::responseForHLSControl(HTTPRequest *request, SOCKADDR_IN *client, int tuner, int channel, String *preset)\r
3824 {\r
3825     DebugLog2("Controller::responseForHLSControl()");\r
3826 \r
3827     HTTPResponse *result = NULL;\r
3828 \r
3829     // client からホスト名を取得\r
3830     char hostname[NI_MAXHOST];\r
3831     if (getnameinfo((SOCKADDR *)client, sizeof(SOCKADDR_IN), hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)\r
3832     {\r
3833         // ストリーミング制御情報からHLS情報を取得\r
3834         Dictionary *hls_info = _streaming_ctrls->dictionaryForKey(KEY_HLS_INFO);\r
3835         if (hls_info == NULL)\r
3836         {\r
3837             hls_info = Dictionary::dictionaryWithCapacity(0);\r
3838             _streaming_ctrls->setObject(hls_info, KEY_HLS_INFO);\r
3839         }\r
3840 \r
3841         // HLS情報から指定チューナの情報を取得\r
3842         Dictionary *hls_info_tuner = hls_info->dictionaryForKey(_tuners[tuner]->name());\r
3843         if (hls_info_tuner == NULL)\r
3844         {\r
3845             hls_info_tuner = Dictionary::dictionaryWithCapacity(0);\r
3846             hls_info->setObject(hls_info_tuner, _tuners[tuner]->name());\r
3847         }\r
3848 \r
3849         // 指定チューナの情報からホスト名を確認\r
3850         if ((hls_info_tuner->stringForKey(KEY_HOSTNAME) == NULL) || hls_info_tuner->stringForKey(KEY_HOSTNAME)->isEqualToString(hostname))\r
3851         {\r
3852              // ホスト名を設定\r
3853             hls_info_tuner->setString(hostname, KEY_HOSTNAME);\r
3854 \r
3855             // HLS制御インスタンス取得\r
3856             HTTPLiveStreaming *hls = (HTTPLiveStreaming *)hls_info_tuner->objectForKey(KEY_HLS_INSTANCE);\r
3857             if (hls == NULL)\r
3858             {\r
3859                 // 生成\r
3860                 hls = HTTPLiveStreaming::alloc()->init()->autorelease();\r
3861                 hls_info_tuner->setObject(hls, KEY_HLS_INSTANCE);\r
3862             }\r
3863 \r
3864             // 異チャンネルへのリクエスト、または、プリセット変更の場合\r
3865             if (((hls_info_tuner->integerForKey(KEY_CHANNEL) != 0) && (hls_info_tuner->integerForKey(KEY_CHANNEL) != channel)) ||\r
3866                 ((hls_info_tuner->stringForKey(KEY_PRESET) != NULL) && !hls_info_tuner->stringForKey(KEY_PRESET)->isEqualToString(preset)))\r
3867             {\r
3868                 // 停止\r
3869                 hls->stop();\r
3870 \r
3871                 // チャンネルとプリセットは一旦削除\r
3872                 hls_info_tuner->removeObjectForKey(KEY_CHANNEL);\r
3873                 hls_info_tuner->removeObjectForKey(KEY_PRESET);\r
3874             }\r
3875 \r
3876             // 初回リクエスト or チャンネル/プリセット変更 か?\r
3877             if ((hls_info_tuner->integerForKey(KEY_CHANNEL) == 0) && (hls_info_tuner->stringForKey(KEY_PRESET) == NULL))\r
3878             {\r
3879                 // UDPポートを検索\r
3880                 Dictionary *mapping = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
3881                 if (mapping != NULL)\r
3882                 {\r
3883                     Array *ports = mapping->allKeys();\r
3884                     if (ports != NULL)\r
3885                     {\r
3886                         char tuner_and_channel[10];\r
3887                         sprintf_s(tuner_and_channel, "%d,%d", tuner, channel);\r
3888 \r
3889                         for (uint i = 0; i < ports->count(); ++i)\r
3890                         {\r
3891                             String *port = (String *)ports->objectAtIndex(i);\r
3892                             String *tmp = mapping->stringForKey(port);\r
3893                             if (tmp->isEqualToString(tuner_and_channel))\r
3894                             {\r
3895                                 DebugLog0("udp mapping %d -> %d,%d", port->intValue(), tuner, channel);\r
3896 \r
3897                                 // ソースを設定\r
3898                                 hls->setSource(String::stringWithFormat("udp://@0.0.0.0:%d", port->intValue()));\r
3899 \r
3900                                 // トランスコード\r
3901 //                                    hls->setTranscode(_props->dictionaryForKey(KEY_PRESETS)->dictionaryForKey(preset));\r
3902 \r
3903                                 // 出力先を設定\r
3904                                 String *outpath = _props->stringForKey(KEY_CACHE_PATH)->stringByAppendingPathComponent(String::stringWithFormat("%03d", tuner));\r
3905                                 _mkdir(outpath->cString());\r
3906                                 hls->setOutputPath(outpath);\r
3907 \r
3908                                 // インデックス名\r
3909                                 hls->setIndexName(String::stringWithFormat("live_%03d_%03d", tuner, channel));\r
3910 \r
3911                                 // 開始\r
3912                                 if (hls->start())\r
3913                                 {\r
3914                                     DebugLog0("hls->start() success");\r
3915 \r
3916                                     // チャンネルを保存\r
3917                                     hls_info_tuner->setInteger(channel, KEY_CHANNEL);\r
3918 \r
3919                                     // プリセットを保存\r
3920                                     hls_info_tuner->setString(preset, KEY_PRESET);\r
3921                                 }\r
3922 \r
3923                                 break;\r
3924                             }\r
3925                         }\r
3926                     }\r
3927                 }\r
3928             }\r
3929 \r
3930             RaymUnlock(this);\r
3931 \r
3932             String *index_path = hls->indexPath();\r
3933             FileManager *fm = FileManager::defaultManager();\r
3934             int count = 0;\r
3935             while (count++ < 30)\r
3936             {\r
3937                 bool isDirectory = false;\r
3938                 if (fm->fileExistsAtPath(index_path, &isDirectory))\r
3939                 {\r
3940                     if (!isDirectory)\r
3941                     {\r
3942                         DebugLog0("file exists");\r
3943                         bool done = false;\r
3944                         while (!done)\r
3945                         {\r
3946                             RaymLock(this);\r
3947                             result = _httpd->responseWithPath(index_path, request);\r
3948                             RaymUnlock(this);\r
3949                             if (result != NULL)\r
3950                             {\r
3951                                 break;\r
3952                             }\r
3953                             ::Sleep(100);\r
3954                         }\r
3955                         break;\r
3956                     }\r
3957                 }\r
3958                 ::Sleep(1000);\r
3959             }\r
3960 \r
3961             RaymLock(this);\r
3962 \r
3963             if (result == NULL)\r
3964             {\r
3965                 DebugLog0("file no exists");\r
3966                 result = responseForReloadURI(request, client, request->URI()->cString(), 10);\r
3967             }\r
3968             else\r
3969             {\r
3970                 hls_info_tuner->setInteger(0, KEY_COUNTER);\r
3971             }\r
3972         }\r
3973         else\r
3974         {\r
3975             // 他ホストが使用中\r
3976         }\r
3977     }\r
3978 \r
3979     return result;\r
3980 }\r
3981 \r
3982 HTTPResponse *Controller::requestTunerControl(HTTPRequest *request, SOCKADDR_IN *client, int tuner)\r
3983 {\r
3984     DebugLog2("%s\n", __FUNCTION__);\r
3985 \r
3986     HTTPResponse *result = NULL;\r
3987 \r
3988     // lock\r
3989     RaymLock(this);\r
3990 \r
3991     // URI取得\r
3992     String *uri = request->URI();\r
3993     while (uri != NULL)\r
3994     {\r
3995         // CGIリクエストとして解析\r
3996         Dictionary *cgi = request->parseAsCGI();\r
3997         if (cgi != NULL)\r
3998         {\r
3999             uri = cgi->stringForKey(HTTPRequest::KEY_CGI);\r
4000             if (uri == NULL)\r
4001             {\r
4002                 break;\r
4003             }\r
4004         }\r
4005 \r
4006         //\r
4007         // チューナ情報\r
4008         //\r
4009         if (uri->isMatch("^/[0-9]{3}/info.xml$") && (cgi == NULL))\r
4010         {\r
4011         }\r
4012 \r
4013         //\r
4014         // チャンネル設定\r
4015         //   /ttt/channel=nnn\r
4016         //\r
4017         else if (uri->isMatch("^/[0-9]{3}/channel=[0-9]{1,3}$") && (cgi == NULL))\r
4018         {\r
4019 DebugLog0("ch set");\r
4020             String *ch = uri->substringFromIndex(13);\r
4021             if (ch == NULL)\r
4022             {\r
4023                 break;\r
4024             }\r
4025             int channel = ch->intValue();\r
4026 DebugLog0("set %d channel:%d(%s)\n", tuner, channel, ch->cString());\r
4027             DebugLog2("set channel:%d(%s)\n", channel, ch->cString());\r
4028             if (setChannel(tuner, channel))\r
4029             {\r
4030                 // success\r
4031                 DebugLog2("success.\n");\r
4032                 result = responseForSuccess(request);\r
4033             }\r
4034             else\r
4035             {\r
4036                 // failed\r
4037                 DebugLog2("failed.\n");\r
4038                 result = responseForFailed(request);\r
4039             }\r
4040         }\r
4041 \r
4042         //\r
4043         // 録画開始(最大23:59まで)\r
4044         //   /ttt/recording=on?hour=hh&min=mm[&channel=nnn]\r
4045         //\r
4046         else if (uri->isMatch("^/[0-9]{3}/recording=on$") && (cgi != NULL))\r
4047         {\r
4048             // パラメータがあるか\r
4049             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
4050             if (params == NULL)\r
4051             {\r
4052                 break;\r
4053             }\r
4054 \r
4055             // パラメータ数は2〜3か\r
4056             if ((params->count() != 2) && (params->count() != 3))\r
4057             {\r
4058                 break;\r
4059             }\r
4060 \r
4061             // パラメータのチェック\r
4062             String *p_hour    = NULL;\r
4063             String *p_min     = NULL;\r
4064             String *p_channel = NULL;\r
4065 \r
4066             struct {\r
4067                 const char *name;\r
4068                 String **variable;\r
4069                 const char *regex;\r
4070             }\r
4071             cgi[] =\r
4072             {\r
4073                 {"hour",    &p_hour,    "^[0-2][0-9]$"},\r
4074                 {"min",     &p_min,     "^[0-5][0-9]$"},\r
4075                 {"channel", &p_channel, "^[0-9]{3}$"},\r
4076                 {NULL, NULL, NULL}\r
4077             };\r
4078 \r
4079             for (uint i = 0; cgi[i].name != NULL; ++i)\r
4080             {\r
4081                 *(cgi[i].variable) = NULL;\r
4082                 for (uint j = 0; j < params->count(); ++j)\r
4083                 {\r
4084                     Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
4085                     String *value = param->stringForKey(cgi[i].name);\r
4086                     if ((value != NULL) && value->isMatch(cgi[i].regex))\r
4087                     {\r
4088                         *(cgi[i].variable) = value;\r
4089                     }\r
4090                 }\r
4091             }\r
4092 \r
4093             // パラメータは有効か\r
4094             if ((p_hour == NULL) || (p_min == NULL))\r
4095             {\r
4096                 break;\r
4097             }\r
4098 \r
4099             // チャンネル設定\r
4100             int channel = 0;\r
4101             if (p_channel != NULL)\r
4102             {\r
4103                 channel = p_channel->intValue();\r
4104             }\r
4105             else\r
4106             {\r
4107                 channel = _tuners[tuner]->channel();\r
4108             }\r
4109 \r
4110             if (channel >= 0)\r
4111             {\r
4112                 // recording on\r
4113                 int hour = p_hour->intValue();\r
4114                 int min = p_min->intValue();\r
4115                 if (hour < 24)\r
4116                 {\r
4117                     // EPG生成\r
4118                     Dictionary *epg = Dictionary::dictionaryWithCapacity(0);\r
4119                     while (true)\r
4120                     {\r
4121                         time_t now;\r
4122                         time(&now);\r
4123                         now += 1; // margin\r
4124                         TM tm;\r
4125                         if (localtime_s(&tm, &now) != 0)\r
4126                         {\r
4127                             epg = NULL;\r
4128                             break;\r
4129                         }\r
4130                         TM end;\r
4131                         end = tm;\r
4132                         end.tm_hour += hour;\r
4133                         end.tm_min += min;\r
4134                         end.tm_sec += 1; // margin\r
4135                         if (mktime(&end) == -1)\r
4136                         {\r
4137                             epg = NULL;\r
4138                             break;\r
4139                         }\r
4140 \r
4141                         char tmp[16];\r
4142 \r
4143                         // Date\r
4144                         sprintf_s(tmp, sizeof(tmp), "%04d/%02d/%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);\r
4145                         epg->setString(tmp, KEY_EPG_DATE);\r
4146 \r
4147                         // Start\r
4148                         sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);\r
4149                         epg->setString(tmp, KEY_EPG_START);\r
4150 \r
4151                         // End\r
4152                         sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", end.tm_hour, end.tm_min, end.tm_sec);\r
4153                         epg->setString(tmp, KEY_EPG_END);\r
4154 \r
4155                         // Channel\r
4156                         sprintf_s(tmp, sizeof(tmp), "%d", channel);\r
4157                         epg->setString(tmp, KEY_EPG_CHANNEL);\r
4158 \r
4159                         // 繰り返し\r
4160                         epg->setString("off", KEY_EPG_REPEAT); \r
4161 \r
4162                         // Status\r
4163                         epg->setString("ready", KEY_EPG_STATUS);\r
4164 \r
4165                         break;\r
4166                     }\r
4167                 \r
4168                     if (epg != NULL)\r
4169                     {\r
4170                         // 録画開始&結果生成\r
4171                         result = responseByResultAndReferer(request, reserve(tuner, epg), URI_RESERVATION_HTML);\r
4172                     }\r
4173                 }\r
4174             }\r
4175         }\r
4176 \r
4177         //\r
4178         // 録画停止\r
4179         //   /ttt/recording=off\r
4180         //\r
4181         else if (uri->isMatch("^/[0-9]{3}/recording=off$") && (cgi == NULL))\r
4182         {\r
4183             // recording off\r
4184             DebugLog2("recording off: %s\n", uri->cString());\r
4185             if (cancel(tuner, -1))\r
4186             {\r
4187                 // success\r
4188                 DebugLog2("success.\n");\r
4189                 result = responseForSuccess(request);\r
4190             }\r
4191             else\r
4192             {\r
4193                 // failed\r
4194                 DebugLog2("failed.\n");\r
4195                 result = responseForFailed(request);\r
4196             }\r
4197         }\r
4198 \r
4199         //\r
4200         // ストリーミング開始\r
4201         //   /ttt/streaming=on?udp=nnnnn(&host=aaaaaa)\r
4202         //\r
4203         else if (uri->isMatch("^/[0-9]{3}/streaming=on$") && (cgi != NULL))\r
4204         {\r
4205             // パラメータがあるか\r
4206             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
4207             if (params == NULL)\r
4208             {\r
4209                 break;\r
4210             }\r
4211 \r
4212             // パラメータ数は1〜2か\r
4213             if ((params->count() != 1) && (params->count() != 2))\r
4214             {\r
4215                 break;\r
4216             }\r
4217 \r
4218             // パラメータのチェック\r
4219             String *p_udp  = NULL;\r
4220             String *p_host = NULL;\r
4221 \r
4222             struct {\r
4223                 const char *name;\r
4224                 String **variable;\r
4225                 const char *regex;\r
4226             }\r
4227             cgi[] =\r
4228             {\r
4229                 {"udp",  &p_udp,  "^[0-9]{1,5}$"},\r
4230                 {"host", &p_host, "^.+$"},\r
4231                 {NULL, NULL, NULL}\r
4232             };\r
4233 \r
4234             for (uint i = 0; cgi[i].name != NULL; ++i)\r
4235             {\r
4236                 *(cgi[i].variable) = NULL;\r
4237                 for (uint j = 0; j < params->count(); ++j)\r
4238                 {\r
4239                     Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
4240                     String *value = param->stringForKey(cgi[i].name);\r
4241                     if ((value != NULL) && value->isMatch(cgi[i].regex))\r
4242                     {\r
4243                         *(cgi[i].variable) = value;\r
4244                     }\r
4245                 }\r
4246             }\r
4247 \r
4248             // パラメータチェック\r
4249             if (p_udp == NULL)\r
4250             {\r
4251                 break;\r
4252             }\r
4253 \r
4254             SOCKADDR_IN dst_addr;\r
4255             \r
4256             if (p_host != NULL)\r
4257             {\r
4258     #if 0\r
4259                 std::string host = udpstr.substr(idx + 5);\r
4260                 udpstr = udpstr.substr(0, idx - 1);\r
4261                 DebugLog2("udp: %s\n", udpstr.c_str());\r
4262                 DebugLog2("host: %s\n", host.c_str());\r
4263                 struct hostent *ent = gethostbyname(host.c_str());\r
4264     #endif\r
4265             }\r
4266             else\r
4267             {\r
4268                 memcpy(&dst_addr, client, sizeof(SOCKADDR_IN));\r
4269             }\r
4270             dst_addr.sin_port = htons(p_udp->intValue());\r
4271             \r
4272             if (_tuners[tuner]->startStreaming(&dst_addr))\r
4273             {\r
4274                 // success\r
4275                 DebugLog2("success.\n");\r
4276                 result = responseForSuccess(request);\r
4277             }\r
4278             else\r
4279             {\r
4280                 // failed\r
4281                 DebugLog2("failed.\n");\r
4282                 result = responseForFailed(request);\r
4283             }\r
4284         }\r
4285 \r
4286         //\r
4287         // ストリーミング停止\r
4288         //   /ttt/streaming=off(?host=aaaa)\r
4289         //\r
4290         else if (uri->isMatch("^/[0-9]{3}/streaming=off$"))\r
4291         {\r
4292             // パラメータ\r
4293             String *p_host = NULL;\r
4294 \r
4295             // パラメータがあるか\r
4296             if (cgi != NULL)\r
4297             {\r
4298                 Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
4299                 if (params == NULL)\r
4300                 {\r
4301                     break;\r
4302                 }\r
4303 \r
4304                 // パラメータ数は0〜1か\r
4305                 if ((params->count() != 0) && (params->count() != 1))\r
4306                 {\r
4307                     break;\r
4308                 }\r
4309 \r
4310                 struct {\r
4311                     const char *name;\r
4312                     String **variable;\r
4313                     const char *regex;\r
4314                 }\r
4315                 cgi[] =\r
4316                 {\r
4317                     {"host", &p_host, "^.+$"},\r
4318                     {NULL, NULL, NULL}\r
4319                 };\r
4320 \r
4321                 for (uint i = 0; cgi[i].name != NULL; ++i)\r
4322                 {\r
4323                     *(cgi[i].variable) = NULL;\r
4324                     for (uint j = 0; j < params->count(); ++j)\r
4325                     {\r
4326                         Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
4327                         String *value = param->stringForKey(cgi[i].name);\r
4328                         if ((value != NULL) && value->isMatch(cgi[i].regex))\r
4329                         {\r
4330                             *(cgi[i].variable) = value;\r
4331                         }\r
4332                     }\r
4333                 }\r
4334             }\r
4335 \r
4336             SOCKADDR_IN dst_addr;\r
4337             if (p_host != NULL)\r
4338             {\r
4339             }\r
4340             else\r
4341             {\r
4342             }\r
4343 \r
4344             _tuners[tuner]->stopStreaming();\r
4345             \r
4346             // success\r
4347             DebugLog2("success.\n");\r
4348             result = responseForSuccess(request);\r
4349         }\r
4350 \r
4351         //\r
4352         // HLS制御\r
4353         //\r
4354         else if (uri->isMatch("^/[0-9]{3}/[0-9]{3}/streaming(-[^\\.]+)?.m3u8$") && (cgi == NULL))\r
4355         {\r
4356             // URIからチャンネル番号を抽出\r
4357             //   Range実装したい...\r
4358             int ch = uri->substringFromIndex(5)->substringToIndex(3)->intValue();\r
4359 \r
4360             // presetが指定されている場合、presetを抽出\r
4361             String *preset = NULL;\r
4362             if (uri->isMatch("streaming-"))\r
4363             {\r
4364                 preset = uri->substringFromIndex(19);\r
4365                 preset = preset->substringToIndex(preset->length() - 5);\r
4366             }\r
4367             else\r
4368             {\r
4369                 // なければ "default"\r
4370                 preset = String::stringWithUTF8String(KEY_DEFAULT);\r
4371             }\r
4372 \r
4373             // チャンネル/presetが有効か確認\r
4374             if (isChannelEnabled(tuner, ch) &&\r
4375                 (_props->dictionaryForKey(KEY_PRESETS) != NULL) &&\r
4376                 (_props->dictionaryForKey(KEY_PRESETS)->objectForKey(preset) != NULL))\r
4377             {\r
4378                 // \r
4379                 result = responseForHLSControl(request, client, tuner, ch, preset);\r
4380             }\r
4381             else\r
4382             {\r
4383                 result = responseForFailed(request);\r
4384             }\r
4385         }\r
4386         else if (uri->isMatch("^/[0-9]{3}/[0-9]{3}/streaming-[0-9]+.ts$") && (cgi == NULL))\r
4387         {\r
4388             // URIからチャンネル番号を抽出\r
4389             //   Range実装したい...\r
4390             int ch = uri->substringFromIndex(5)->substringToIndex(3)->intValue();\r
4391 \r
4392             // client からホスト名を取得\r
4393             char hostname[NI_MAXHOST];\r
4394             if (getnameinfo((SOCKADDR *)client, sizeof(SOCKADDR_IN), hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)\r
4395             {\r
4396                 Dictionary *hls_info = _streaming_ctrls->dictionaryForKey(KEY_HLS_INFO);\r
4397                 if (hls_info != NULL)\r
4398                 {\r
4399                     Dictionary *hls_info_tuner = hls_info->dictionaryForKey(_tuners[tuner]->name());\r
4400                     if (hls_info_tuner != NULL)\r
4401                     {\r
4402                         if (hls_info_tuner->stringForKey(KEY_HOSTNAME)->isEqualToString(hostname) &&\r
4403                             (hls_info_tuner->integerForKey(KEY_CHANNEL) == ch))\r
4404                         {\r
4405                             String *path = ((HTTPLiveStreaming *)hls_info_tuner->objectForKey(KEY_HLS_INSTANCE))->outputPath();\r
4406                             if (path != NULL)\r
4407                             {\r
4408                                 result = _httpd->responseWithPath(path->stringByAppendingPathComponent(uri->substringFromIndex(9)), request);\r
4409                             }\r
4410                         }\r
4411                     }\r
4412                 }\r
4413             }\r
4414         }\r
4415 \r
4416         break;\r
4417     }\r
4418 \r
4419     // unlock\r
4420     RaymUnlock(this);\r
4421 \r
4422     return result;\r
4423 }\r
4424 \r
4425 HTTPResponse *Controller::request(HTTPRequest *request, SOCKADDR_IN *client)\r
4426 {\r
4427     DebugLog2("%s\n", __FUNCTION__);\r
4428 \r
4429     // 初期化チェック\r
4430     bool flag = false;\r
4431     RaymLock(this);\r
4432     flag = _initialized;\r
4433     RaymUnlock(this);\r
4434     if (!flag)\r
4435     {\r
4436         return NULL;\r
4437     }\r
4438 \r
4439     HTTPResponse *response = NULL;\r
4440 \r
4441     if (request->method()->isEqualToString("GET") ||\r
4442         request->method()->isEqualToString("HEAD"))\r
4443     {\r
4444         // URI\r
4445         String *uri = request->URI();\r
4446         DebugLog0("request: %s\n", uri->cString());\r
4447 \r
4448         //\r
4449         //\r
4450         //\r
4451         if (uri->isMatch("^(/|/index\\.(htm|html))$"))\r
4452         {\r
4453             response = responseForMain(request, client);\r
4454         }\r
4455 \r
4456         else if (uri->isMatch("^/status.html$"))\r
4457         {\r
4458             response = responseForStatus(request, client);\r
4459         }\r
4460 \r
4461         //\r
4462         else if (uri->isMatch("^/info.xml$"))\r
4463         {\r
4464         }\r
4465         else if (uri->isMatch("^/video.xml$"))\r
4466         {\r
4467         }\r
4468 \r
4469         //\r
4470         else if (uri->isMatch("^/suspend.xml$"))\r
4471         {\r
4472             if (canSuspend())\r
4473             {\r
4474 //                response = responseForSuccess(request);\r
4475 //                delaySuspend();\r
4476             }\r
4477             else\r
4478             {\r
4479 //                response = responseForFailed(request);\r
4480             }\r
4481         }\r
4482 \r
4483         //\r
4484         else if (uri->isMatch("^/exec_vlc.html$"))\r
4485         {\r
4486 //            _vlc->execute();\r
4487 //            return responseForRefreshMain(request, client);\r
4488         }\r
4489 \r
4490         //\r
4491         // tuner control\r
4492         //\r
4493         else if (uri->isMatch("^/[0-9]{3}/"))\r
4494         {\r
4495             // String::substringWithRange() を実装するのを後回しにするので。。\r
4496             std::string s = uri->cString();\r
4497             int tuner = atoi(s.substr(1, 3).c_str());\r
4498             if ((0 <= tuner) && (tuner < _tunerCount))\r
4499             {\r
4500                 response = requestTunerControl(request, client, tuner);\r
4501             }\r
4502         }\r
4503 \r
4504         //\r
4505         // reservation control\r
4506         //\r
4507         else if (uri->isMatch("^/reservation.html$"))\r
4508         {\r
4509             response = responseForReservation(request, client);\r
4510         }\r
4511         else if (uri->isMatch("^/regist.cgi"))\r
4512         {\r
4513             response = responseForRegistCGI(request, client);\r
4514         }\r
4515         else if (uri->isMatch("^/cancel.cgi"))\r
4516         {\r
4517             response = responseForCancelCGI(request, client);\r
4518         }\r
4519         else if (uri->isMatch("^/add_keywords.cgi"))\r
4520         {\r
4521             response = responseForAddKeywordsCGI(request, client);\r
4522         }\r
4523         else if (uri->isMatch("^/mod_keywords.cgi"))\r
4524         {\r
4525             response = responseForModKeywordsCGI(request, client);\r
4526         }\r
4527         else if (uri->isMatch("^/reservation.xml$"))\r
4528         {\r
4529         }\r
4530         else if (uri->isMatch("^/[0-9]{6}/"))\r
4531         {\r
4532 //            DebugLog2("reservation: %s\n", uri.c_str());\r
4533         }\r
4534 \r
4535         //\r
4536         // video control\r
4537         //\r
4538         else if (uri->isMatch("^/[0-9]{9}/"))\r
4539         {\r
4540 //            DebugLog2("video: %s\n", uri.c_str());\r
4541         }\r
4542 \r
4543         //\r
4544         // program\r
4545         //\r
4546 //        else if (match(uri.c_str(), "^/programs.html$"))\r
4547         else if (uri->isMatch("^/" URI_PROGRAMS_HTML "$"))\r
4548         {\r
4549             response = responseForPrograms(request, client);\r
4550         }\r
4551         else if (uri->isMatch("^/programs.xml$"))\r
4552         {\r
4553 //            collectEPG();\r
4554 //            return responseWithDictionary(request, _programs);\r
4555         }\r
4556 \r
4557         //\r
4558         //\r
4559         else if (uri->isMatch("^/tv.html$"))\r
4560         {\r
4561 //            return responseForTV(request, client);\r
4562         }\r
4563 \r
4564         // IPTV対応\r
4565         else if (uri->isMatch("^/iptv.m3u8$"))\r
4566         {\r
4567             return responseForPlaylist(request, client);\r
4568         }\r
4569 \r
4570         //\r
4571         else if (uri->isMatch("^/iptd.log$"))\r
4572         {\r
4573             String *path = _system_path->stringByAppendingPathComponent("log");\r
4574             if (path != NULL)\r
4575             {\r
4576                 path = path->stringByAppendingPathComponent("iptd.log");\r
4577                 if (path != NULL)\r
4578                 {\r
4579                     response = _httpd->responseWithPath(path, request);\r
4580                     if (response != NULL)\r
4581                     {\r
4582                         if (response->message() != NULL)\r
4583                         {\r
4584                             if (response->message()->header() != NULL)\r
4585                             {\r
4586                                 response->message()->header()->setFieldBodyWithName("text/plane; charset=UTF-8", "Content-Type");\r
4587                             }\r
4588                         }\r
4589                     }\r
4590                 }\r
4591             }\r
4592         }\r
4593     }\r
4594     else if (request->method()->isEqualToString("POST"))\r
4595     {\r
4596         // URI\r
4597         String *uri = request->URI();\r
4598         DebugLog1("POST: %s\n", uri->cString());\r
4599         InternetTextMessage *message = request->message();\r
4600         if (message != NULL)\r
4601         {\r
4602             DebugLog3("message: ");\r
4603             InternetTextMessageHeader *header = message->header();\r
4604             if (header != NULL)\r
4605             {\r
4606                 DebugLog3("header: ");\r
4607             }\r
4608             InternetTextMessageBody *body = message->body();\r
4609             if (body != NULL)\r
4610             {\r
4611                 DebugLog3("body: ");\r
4612             }\r
4613         }\r
4614     }\r
4615 \r
4616     return response;\r
4617 }\r
4618 \r
4619 #ifndef _WIN32\r
4620 #pragma mark '\r
4621 #pragma mark ------- プロパティ取得 -------\r
4622 #endif\r
4623 \r
4624 bool Controller::isTunerInitialized(int tuner)\r
4625 {\r
4626     DebugLog2("Controller::isTunerInitialized()");\r
4627 \r
4628     bool result = false;\r
4629 \r
4630     if ((0 <= tuner) && (tuner < _tunerCount))\r
4631     {\r
4632         // lock\r
4633         RaymLock(this);\r
4634 \r
4635         Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
4636         if (tunersInfo != NULL)\r
4637         {\r
4638             Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
4639             if (tunerInfo != NULL)\r
4640             {\r
4641                 result = tunerInfo->boolForKey(KEY_INITIALIZED);\r
4642             }\r
4643         }\r
4644 \r
4645         // unlock\r
4646         RaymUnlock(this);\r
4647     }\r
4648 \r
4649     return result;\r
4650 }\r
4651 \r
4652 bool Controller::isTunerEnabled(int tuner)\r
4653 {\r
4654     DebugLog2("Controller::isTunerEnabled()");\r
4655 \r
4656     bool result = false;\r
4657 \r
4658     if ((0 <= tuner) && (tuner < _tunerCount))\r
4659     {\r
4660         // lock\r
4661         RaymLock(this);\r
4662 \r
4663         Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
4664         if (tunersInfo != NULL)\r
4665         {\r
4666             Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
4667             if (tunerInfo != NULL)\r
4668             {\r
4669                 result = tunerInfo->boolForKey(KEY_ENABLED);\r
4670             }\r
4671         }\r
4672 \r
4673         // unlock\r
4674         RaymUnlock(this);\r
4675     }\r
4676 \r
4677     return result;\r
4678 }\r
4679 \r
4680 bool Controller::isChannelEnabled(int tuner, int channel)\r
4681 {\r
4682     DebugLog2("Controller::isChannelEnabled()");\r
4683 \r
4684     bool result = false;\r
4685 \r
4686     if ((0 <= tuner) && (tuner < _tunerCount) && (0 <= channel) && (channel <= Tuner::MAX_CHANNELS_ISDB_T))\r
4687     {\r
4688         // lock\r
4689         RaymLock(this);\r
4690 \r
4691         Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
4692         if (tunersInfo != NULL)\r
4693         {\r
4694             Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
4695             if (tunerInfo != NULL)\r
4696             {\r
4697                 Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
4698                 if (channels != NULL)\r
4699                 {\r
4700                     char key[4];\r
4701                     sprintf_s(key, "%03d", channel);\r
4702                     Dictionary *chInfo = channels->dictionaryForKey(key);\r
4703                     if (chInfo != NULL)\r
4704                     {\r
4705                         result = chInfo->boolForKey(KEY_ENABLED);\r
4706                     }\r
4707                 }\r
4708             }\r
4709         }\r
4710 \r
4711         // unlock\r
4712         RaymUnlock(this);\r
4713     }\r
4714 \r
4715     return result;\r
4716 }\r
4717 \r
4718 Array *Controller::stationInfos(Tuner::Type type)\r
4719 {\r
4720     DebugLog2("Controller::stationInfosForISDB_T()");\r
4721 \r
4722     Array *result = Array::arrayWithCapacity(0);\r
4723 \r
4724     // lock\r
4725     RaymLock(this);\r
4726 \r
4727     Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
4728     if (tunersInfo != NULL)\r
4729     {\r
4730         for (int i = 0; i < _tunerCount; ++i)\r
4731         {\r
4732             if (_tuners[i]->type() == type)\r
4733             {\r
4734                 Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[i]->name());\r
4735                 if (tunerInfo != NULL)\r
4736                 {\r
4737                     Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
4738                     if (channels != NULL)\r
4739                     {\r
4740                         for (uint ch = 0; ch <= (type == Tuner::ISDB_T ? Tuner::MAX_CHANNELS_ISDB_T : Tuner::MAX_CHANNELS_ISDB_S); ++ch)\r
4741                         {\r
4742                             char chkey[4];\r
4743                             sprintf_s(chkey, "%03d", ch);\r
4744                             Dictionary *channel = channels->dictionaryForKey(chkey);\r
4745                             if ((channel != NULL) && channel->boolForKey(KEY_ENABLED))\r
4746                             {\r
4747                                 result->addObject(channel);\r
4748                             }\r
4749                         }\r
4750                     }\r
4751                 }\r
4752                 break;\r
4753             }\r
4754         }\r
4755     }\r
4756 \r
4757     // unlock\r
4758     RaymUnlock(this);\r
4759 \r
4760     return result;\r
4761 }\r
4762 \r
4763 String *Controller::stationName(Tuner::Type type, int channel)\r
4764 {\r
4765     DebugLog2("Controller::stationName()");\r
4766 \r
4767     String *result = NULL;\r
4768 \r
4769     // lock\r
4770     RaymLock(this);\r
4771 \r
4772     Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
4773     if (tunersInfo != NULL)\r
4774     {\r
4775         for (int i = 0; i < _tunerCount; ++i)\r
4776         {\r
4777             if (_tuners[i]->type() == type)\r
4778             {\r
4779                 Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[i]->name());\r
4780                 if (tunerInfo != NULL)\r
4781                 {\r
4782                     Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
4783                     if (channels != NULL)\r
4784                     {\r
4785                         char chkey[4];\r
4786                         sprintf_s(chkey, "%03d", channel);\r
4787                         Dictionary *channel = channels->dictionaryForKey(chkey);\r
4788                         if ((channel != NULL) && channel->boolForKey(KEY_ENABLED))\r
4789                         {\r
4790                             result = channel->stringForKey(KEY_NAME);\r
4791                         }\r
4792                     }\r
4793                 }\r
4794                 break;\r
4795             }\r
4796         }\r
4797     }\r
4798 \r
4799     // unlock\r
4800     RaymUnlock(this);\r
4801 \r
4802     return result;\r
4803 }\r
4804 \r
4805 // epgの開始時刻でソートする為の比較関数\r
4806 Integer compareFunction(Object *obj1, Object *obj2, void *context)\r
4807 {\r
4808     if (isKindOfClass(Dictionary, obj1) && isKindOfClass(Dictionary, obj2))\r
4809     {\r
4810         time_t st1;\r
4811         time_t ed1;\r
4812         Controller::getTimeWithEPG((Dictionary *)obj1, &st1, &ed1);\r
4813 \r
4814         time_t st2;\r
4815         time_t ed2;\r
4816         Controller::getTimeWithEPG((Dictionary *)obj2, &st2, &ed2);\r
4817 \r
4818         if (st1 < st2)\r
4819         {\r
4820             return OrderedAscending;\r
4821         }\r
4822         else if (st1 > st2)\r
4823         {\r
4824             return OrderedDescending;\r
4825         }\r
4826         else\r
4827         {\r
4828             if (ed1 < ed2)\r
4829             {\r
4830                 return OrderedAscending;\r
4831             }\r
4832             else if (ed1 > ed2)\r
4833             {\r
4834                 return OrderedDescending;\r
4835             }\r
4836         }\r
4837     }\r
4838     return OrderedSame;\r
4839 }\r
4840 \r
4841 Array *Controller::programsForServices(Array *services)\r
4842 {\r
4843     DebugLog2("Controller::programsForServices()");\r
4844 \r
4845     Array *result = Array::arrayWithCapacity(0);\r
4846 \r
4847     // lock\r
4848     RaymLock(this);\r
4849 \r
4850     for (uint i = 0; i < services->count(); ++i)\r
4851     {\r
4852         Dictionary *service = (Dictionary *)services->objectAtIndex(i);\r
4853         Dictionary *events = _epgs->dictionaryForKey(service->stringForKey(KEY_SERVICE_ID));\r
4854         if (events != NULL)\r
4855         {\r
4856             Array *keys = events->allKeys();\r
4857             for (uint j = 0; j < keys->count(); ++j)\r
4858             {\r
4859                 result->addObject(events->objectForKey((String *)keys->objectAtIndex(j)));\r
4860             }\r
4861         }\r
4862     }\r
4863 \r
4864     // sort\r
4865     result = result->sortedArrayUsingFunction(compareFunction, this);\r
4866 \r
4867 \r
4868     // unlock\r
4869     RaymUnlock(this);\r
4870 \r
4871     return result;\r
4872 }\r
4873 \r
4874 String *Controller::stationNameForServiceID(String *service_id)\r
4875 {\r
4876     DebugLog2("Controller::stationNameForServiceID()");\r
4877 \r
4878     String *result = NULL;\r
4879 \r
4880     if (service_id != NULL)\r
4881     {\r
4882         // lock\r
4883         RaymLock(this);\r
4884 \r
4885         Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
4886         if (tunersInfo != NULL)\r
4887         {\r
4888             Array *tuners_key = tunersInfo->allKeys();\r
4889 \r
4890             for (uint i = 0; (result == NULL) && (i < tuners_key->count()); ++i)\r
4891             {\r
4892                 Dictionary *tunerInfo = tunersInfo->dictionaryForKey((String *)tuners_key->objectAtIndex(i));\r
4893                 if (tunerInfo != NULL)\r
4894                 {\r
4895                     Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
4896                     if (channels != NULL)\r
4897                     {\r
4898                         Array *chkey = channels->allKeys();\r
4899                         for (uint ch = 0; (result == NULL) && (ch < chkey->count()); ++ch)\r
4900                         {\r
4901                             Dictionary *channel = channels->dictionaryForKey((String *)chkey->objectAtIndex(ch));\r
4902                             if (channel != NULL)\r
4903                             {\r
4904                                 Array *services = channel->arrayForKey(KEY_SERVICES);\r
4905                                 if (services != NULL)\r
4906                                 {\r
4907                                     for (uint j = 0; j < services->count(); ++j)\r
4908                                     {\r
4909                                         Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
4910                                         String *sid = service->stringForKey(KEY_SERVICE_ID);\r
4911                                         if (sid != NULL)\r
4912                                         {\r
4913                                             if (sid->isEqualToString(service_id))\r
4914                                             {\r
4915                                                 result = service->stringForKey(KEY_NAME);\r
4916                                                 break;\r
4917                                             }\r
4918                                         }\r
4919                                     }\r
4920                                 }\r
4921                             }\r
4922                         }\r
4923                     }\r
4924                 }\r
4925             }\r
4926         }\r
4927 \r
4928         // unlock\r
4929         RaymUnlock(this);\r
4930     }\r
4931 \r
4932     return result;\r
4933 }\r
4934 \r
4935 #ifndef _WIN32\r
4936 #pragma mark '\r
4937 #pragma mark ------- タイマディスパッチャ -------\r
4938 #endif\r
4939 \r
4940 void Controller::timerExpired(Timer *timer, void *userInfo)\r
4941 {\r
4942     DebugLog2("Controller::timerExpired()");\r
4943 \r
4944     //\r
4945 \r
4946     switch ((long long)userInfo)\r
4947     {\r
4948     case CMD_RESTART:\r
4949         if (restart() > 0)\r
4950         {\r
4951             // 初期化成功\r
4952             DebugLog2("tuner initialize success.");\r
4953 \r
4954             // EPG収集用タイマ起動(ISDB-S)\r
4955             _timer_epg_s = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);\r
4956             if (_timer_epg_s != NULL)\r
4957             {\r
4958                 _timer_epg_s->fire();\r
4959             }\r
4960 \r
4961             // EPG収集用タイマ起動(ISDB-T)\r
4962             _timer_epg_t = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);\r
4963             if (_timer_epg_t != NULL)\r
4964             {\r
4965                 _timer_epg_t->fire();\r
4966             }\r
4967         }\r
4968         else\r
4969         {\r
4970             // 失敗\r
4971             DebugLog0("tuner initialize failed.");\r
4972         }\r
4973         break;\r
4974 \r
4975     case CMD_SUSPEND:\r
4976         if (canSuspend())\r
4977         {\r
4978             suspend();\r
4979         }\r
4980         break;\r
4981 \r
4982     case CMD_PERIODIC:                                  // 周期処理\r
4983         periodic();\r
4984         break;\r
4985 \r
4986     case CMD_PERIODIC_2:                                // 周期処理2\r
4987         periodic_2();\r
4988         break;\r
4989 \r
4990     case CMD_COLLECT_EPG_ISDB_S:                        // EPG収集(ISDB-S)\r
4991         // 番組情報収集(ISDB-S)\r
4992         if (collectEPGs(Tuner::ISDB_S))\r
4993         {\r
4994             // 終了(繰り返し無し)\r
4995             _timer_epg_s->setRepeats(false);\r
4996         }\r
4997         else\r
4998         {\r
4999             // リトライ\r
5000             _timer_epg_s->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
5001         }\r
5002         break;\r
5003 \r
5004     case CMD_COLLECT_EPG_ISDB_T:                        // 番組情報収集(ISDB-T)\r
5005         if (collectEPGs(Tuner::ISDB_T))\r
5006         {\r
5007             // 終了(繰り返し無し)\r
5008             _timer_epg_t->setRepeats(false);\r
5009         }\r
5010         else\r
5011         {\r
5012             // リトライ\r
5013             _timer_epg_t->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
5014         }\r
5015         break;\r
5016     }\r
5017 }\r
5018 \r
5019 #ifndef _WIN32\r
5020 #pragma mark '\r
5021 #pragma mark ------- 起動/停止関連 -------\r
5022 #endif\r
5023 \r
5024 // システム状態をチェックしてサスペンド可能かを返す\r
5025 bool Controller::canSuspend()\r
5026 {\r
5027     return isIdleState(true);\r
5028 }\r
5029 \r
5030 bool Controller::canTerminate()\r
5031 {\r
5032     return isIdleState(false);\r
5033 }\r
5034 \r
5035 bool Controller::isIdleState(bool suspend)\r
5036 {\r
5037     DebugLog2("Controller::isIdleState() start.");\r
5038 \r
5039     bool result = false;\r
5040 \r
5041     // lock\r
5042     RaymLock(this);\r
5043 \r
5044     // 初期化済みか\r
5045     while (_initialized)\r
5046     {\r
5047         // EPG(ISDB-S)収集中か\r
5048         if (suspend && (_timer_epg_s != NULL) && _timer_epg_s->valid())\r
5049         {\r
5050             // サスペンド不可\r
5051             break;\r
5052         }\r
5053 \r
5054         // EPG(ISDB-T)収集中か\r
5055         if (suspend && (_timer_epg_t != NULL) && _timer_epg_t->valid())\r
5056         {\r
5057             // サスペンド不可\r
5058             break;\r
5059         }\r
5060 \r
5061         // サスペンド可に設定\r
5062         result = true;\r
5063 \r
5064         // 予約情報をチェック\r
5065         // 10分後以内に開始の予約があったらサスペンド不可\r
5066 \r
5067         // 現在時刻取得\r
5068         time_t now = time(NULL);\r
5069 \r
5070         for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
5071         {\r
5072             Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
5073             if ((array == NULL) || (array->count() == 0))\r
5074             {\r
5075                 // next tuner\r
5076                 continue;\r
5077             }\r
5078 \r
5079             //\r
5080             Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
5081             time_t start;\r
5082             time_t end;\r
5083             getTimeWithEPG(epg, &start, &end);\r
5084 \r
5085             if (start + OFFSET_OF_SUPPRESSION_TIME <= now)\r
5086             {\r
5087                 // サスペンド不可\r
5088                 result = false;\r
5089                 break;\r
5090             }\r
5091         }\r
5092 \r
5093         break;\r
5094     }\r
5095 \r
5096     // unlock\r
5097     RaymUnlock(this);\r
5098 \r
5099     DebugLog2("Controller::isIdleState() end.");\r
5100 \r
5101     return result;\r
5102 }\r
5103 \r
5104 /*\r
5105  * TrayApp::WndProc() からコールされる\r
5106  *\r
5107  *   スレッドコンテキスト:メインスレッド\r
5108  */\r
5109 void Controller::systemWillSuspend()\r
5110 {\r
5111     DebugLog2("Controller::systemWillSuspend() start");\r
5112 \r
5113     RaymLock(this);\r
5114     _cancel_epg_collect = true;\r
5115     RaymUnlock(this);\r
5116 \r
5117     // タイマ停止\r
5118     if ((_timer_restart != NULL) && _timer_restart->valid())\r
5119     {\r
5120         _timer_restart->invalidate();\r
5121     }\r
5122     if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
5123     {\r
5124         _timer_epg_s->invalidate();\r
5125     }\r
5126     if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
5127     {\r
5128         _timer_epg_t->invalidate();\r
5129     }\r
5130     if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
5131     {\r
5132         _timer_periodic->invalidate();\r
5133     }\r
5134     if ((_timer_periodic_2 != NULL) && _timer_periodic_2->valid())\r
5135     {\r
5136         _timer_periodic_2->invalidate();\r
5137     }\r
5138 \r
5139     // lock\r
5140     RaymLock(this);\r
5141 \r
5142     // タイマ解放\r
5143     RELEASE(_timer_restart);\r
5144     RELEASE(_timer_epg_s);\r
5145     RELEASE(_timer_epg_t);\r
5146     RELEASE(_timer_periodic);\r
5147     RELEASE(_timer_periodic_2);\r
5148 \r
5149     // スケジュール更新\r
5150     updateSchedule();\r
5151 \r
5152     // 未初期化に設定\r
5153     _initialized = false;\r
5154 \r
5155     // チューナ解放\r
5156     for (int i = 0; i < _tunerCount; ++i)\r
5157     {\r
5158         if (_tuners[i] != NULL)\r
5159         {\r
5160             delete _tuners[i];\r
5161             _tuners[i] = NULL;\r
5162         }\r
5163     }\r
5164 \r
5165     _cancel_epg_collect = false;\r
5166 \r
5167     DebugLog0("system will suspend...");\r
5168 \r
5169     // unlock\r
5170     RaymUnlock(this);\r
5171 \r
5172 #ifdef OBJC_MEMORY_CHECK\r
5173     DebugLog0("global_objc_count_ = %d", Raym::global_objc_count_);\r
5174 #endif\r
5175 \r
5176     DebugLog2("Controller::systemWillSuspend() end");\r
5177 }\r
5178 \r
5179 /*\r
5180  * TrayApp::WndProc() からコールされる\r
5181  *\r
5182  *   スレッドコンテキスト:メインスレッド\r
5183  */\r
5184 void Controller::systemResumed()\r
5185 {\r
5186     DebugLog2("Controller::systemResumed() start");\r
5187 \r
5188     DebugLog0("system resumed.");\r
5189 \r
5190     if (_timer_restart == NULL)\r
5191     {\r
5192         // 再開タイマ起動\r
5193         _timer_restart = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_RESTART, false);\r
5194         _timer_restart->fire();\r
5195     }\r
5196 \r
5197     DebugLog2("Controller::systemResumed()");\r
5198 }\r
5199 \r
5200 /*\r
5201  * TrayApp::WndProc() からコールされる\r
5202  *\r
5203  *   スレッドコンテキスト:メインスレッド\r
5204  */\r
5205 void Controller::detectIdle()\r
5206 {\r
5207     DebugLog2("Controller::detectIdle()");\r
5208 \r
5209     // ここはメインスレッドなのでARPを用意する\r
5210     AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
5211 \r
5212     // lock\r
5213     RaymLock(this);\r
5214 \r
5215     // サスペンド可能か確認\r
5216     if (canSuspend())\r
5217     {\r
5218         if (_idle_count == 0)\r
5219         {\r
5220             DebugLog0("detect idle...");\r
5221         }\r
5222 \r
5223         // アイドルカウンタを更新\r
5224         ++_idle_count;\r
5225 \r
5226         // 起動中アプリと休止状態抑止アプリのチェック\r
5227         bool found = false;\r
5228 \r
5229         Array *dont_in_suspend = _props->arrayForKey(KEY_DO_NOT_IN_SUSPEND);\r
5230         Array *running_apps = Workspace::sharedWorkspace()->runningApplications();\r
5231         for (uint i = 0; (i < running_apps->count()) && !found; ++i)\r
5232         {\r
5233             RunningApplication *ra = (RunningApplication *)running_apps->objectAtIndex(i);\r
5234 \r
5235             // 実行中でなければ次へ\r
5236             if (!ra->isRunning())\r
5237             {\r
5238                 continue;\r
5239             }\r
5240 \r
5241             // 実行ファイルのチェック\r
5242             String *path = ra->executePath();\r
5243             if ((path == NULL) || (path->length() == 0))\r
5244             {\r
5245                 continue;\r
5246             }\r
5247             DebugLog3("exec path: %s", path->cString());\r
5248 \r
5249             // 休止状態抑止アプリリストのチェック\r
5250             for (uint j = 0; (j < dont_in_suspend->count()) && !found; ++j)\r
5251             {\r
5252                 found = path->isMatch((String *)dont_in_suspend->objectAtIndex(j));\r
5253             }\r
5254         }\r
5255 \r
5256         // 抑止有効なら KEY_FORCED_SUSPEND_TIME、無効なら KEY_SUSPEND_TIME で取得\r
5257         int limit = _props->integerForKey(found ? KEY_FORCED_SUSPEND_TIME : KEY_SUSPEND_TIME);\r
5258         DebugLog3("found: %d, _idle_count: %d, limit: %d", found, _idle_count, limit);\r
5259         if (_idle_count >= limit)\r
5260         {\r
5261             // unlock\r
5262             RaymUnlock(this);\r
5263 \r
5264             // サスペンド前にARPを解放しておく\r
5265             pool->release();\r
5266 \r
5267             // 下記の TrayApp::suspend() を使用する場合、自アプリへsuspendメッセージが\r
5268             // ブロードキャストされないため、自分でコールしておく\r
5269             systemWillSuspend();\r
5270 \r
5271             // サスペンド\r
5272             suspend();\r
5273 \r
5274             // 再度 ARPを用意\r
5275             pool = AutoreleasePool::alloc()->init();\r
5276 \r
5277             // lock\r
5278             RaymLock(this);\r
5279 \r
5280             // アイドルカウンタをクリア\r
5281             _idle_count = 0;\r
5282         }\r
5283     }\r
5284     else\r
5285     {\r
5286         // アイドルカウンタをクリア\r
5287         _idle_count = 0;\r
5288     }\r
5289 \r
5290     // unlock\r
5291     RaymUnlock(this);\r
5292 \r
5293     // ARP解放\r
5294     pool->release();\r
5295 }\r
5296 \r
5297 void Controller::detectNonIdle()\r
5298 {\r
5299     DebugLog2("Controller::detectNonIdle()");\r
5300 \r
5301     // lock\r
5302     RaymLock(this);\r
5303 \r
5304     if (_idle_count > 0)\r
5305     {\r
5306         DebugLog0("detect non idle...");\r
5307     }\r
5308     _idle_count = 0;\r
5309 \r
5310     // unlock\r
5311     RaymUnlock(this);\r
5312 }\r
5313 \r
5314 int Controller::restart()\r
5315 {\r
5316     // lock\r
5317     RaymLock(this);\r
5318 \r
5319     // 未初期化に設定\r
5320     _initialized = false;\r
5321 \r
5322     // チューナ解放\r
5323     for (int i = 0; i < _tunerCount; ++i)\r
5324     {\r
5325         if (_tuners[i] != NULL)\r
5326         {\r
5327             delete _tuners[i];\r
5328             _tuners[i] = NULL;\r
5329         }\r
5330     }\r
5331 \r
5332     // 古い予約情報の破棄\r
5333 \r
5334 \r
5335     // チューナ初期化\r
5336     _tunerCount = ry0::device::TunerFactory::scan(_tuners, _multi2_dll);\r
5337 \r
5338     // unlock\r
5339     RaymUnlock(this);\r
5340 \r
5341     // チューナでループ\r
5342     for (int i = 0; i < _tunerCount; ++i)\r
5343     {\r
5344         // チューナが初期化済みか\r
5345         if (!isTunerInitialized(i))\r
5346         {\r
5347             // チャンネルスキャン\r
5348             scanChannel(i);\r
5349         }\r
5350 \r
5351         if (isTunerEnabled(i))\r
5352         {\r
5353             // チャンネル設定\r
5354             setChannel(i, getChannel(i));\r
5355         }\r
5356     }\r
5357 \r
5358     // UDPポートマッピング\r
5359     DebugLog0("  UDP Port Mapping    :");\r
5360     DebugLog0("    tuner, ch -> port");\r
5361     int udpport = _props->integerForKey(KEY_BEGIN_UDP_PORT);\r
5362     for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
5363     {\r
5364         uint max_channel = (_tuners[tuner]->type() == Tuner::ISDB_S ? Tuner::MAX_CHANNELS_ISDB_S : Tuner::MAX_CHANNELS_ISDB_T);\r
5365         for (uint ch = 0; ch <= max_channel; ++ch)\r
5366         {\r
5367             if (isChannelEnabled(tuner, ch))\r
5368             {\r
5369                 DebugLog0("    %5d,%3d -> %5d", tuner, ch, udpport);\r
5370 \r
5371                 Dictionary *udp_to_tuner_channel = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
5372                 if (udp_to_tuner_channel == NULL)\r
5373                 {\r
5374                     udp_to_tuner_channel = Dictionary::dictionaryWithCapacity(0);\r
5375                     _streaming_ctrls->setObject(udp_to_tuner_channel, KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
5376                 }\r
5377 \r
5378                 Dictionary *tuner_channel_to_udp = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
5379                 if (tuner_channel_to_udp == NULL)\r
5380                 {\r
5381                     tuner_channel_to_udp = Dictionary::dictionaryWithCapacity(0);\r
5382                     _streaming_ctrls->setObject(tuner_channel_to_udp, KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
5383                 }\r
5384 \r
5385                 char port_str[10];\r
5386                 sprintf_s(port_str, "%d", udpport);\r
5387                 char tuner_and_channel[10];\r
5388                 sprintf_s(tuner_and_channel, "%d,%d", tuner, ch);\r
5389 \r
5390                 udp_to_tuner_channel->setString(tuner_and_channel, port_str);\r
5391                 tuner_channel_to_udp->setString(port_str, tuner_and_channel);\r
5392 \r
5393                 ++udpport;\r
5394             }\r
5395         }\r
5396     }\r
5397 \r
5398     // 周期タイマ起動\r
5399     _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC, true);\r
5400     _timer_periodic->fire();\r
5401 \r
5402     // 周期タイマ2起動\r
5403     _timer_periodic_2 = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC_2, true);\r
5404     _timer_periodic_2->fire();\r
5405 \r
5406     // lock\r
5407     RaymLock(this);\r
5408 \r
5409     // 初期化済みに設定\r
5410     _initialized = true;\r
5411 \r
5412     // unlock\r
5413     RaymUnlock(this);\r
5414 \r
5415     return (_tunerCount > 0);\r
5416 }\r
5417 \r
5418 int Controller::start()\r
5419 {\r
5420     // ログファイル数設定\r
5421     //   設定以前にログ出力しないこと\r
5422     Raym::LOG_NUM_MAX = 8;\r
5423 \r
5424     HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());\r
5425     if (hProcess != NULL)\r
5426     {\r
5427         if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS) == FALSE)\r
5428         {\r
5429             DebugLog0("SetPriorityClass failed.");\r
5430         }\r
5431         CloseHandle(hProcess);\r
5432     }\r
5433 \r
5434 #ifdef RAYM_MEMORY_CHECK\r
5435     DebugLog0("");\r
5436     DebugLog0("global_raym_count_ = %d", Raym::global_raym_count_);\r
5437 #endif\r
5438 \r
5439     //\r
5440     AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
5441 \r
5442     // メンバ初期化\r
5443     _system_path        = NULL;\r
5444     _props_path         = NULL;\r
5445     _props              = NULL;\r
5446     _status_path        = NULL;\r
5447     _status             = NULL;\r
5448     _epgs_path          = NULL;\r
5449     _epgs               = NULL;\r
5450     _store_path         = NULL;\r
5451     _reservations       = NULL;\r
5452     _reservations_path  = NULL;\r
5453     _timer_restart      = NULL;\r
5454     _timer_periodic     = NULL;\r
5455     _timer_periodic_2   = NULL;\r
5456     _timer_epg_s        = NULL;\r
5457     _timer_epg_t        = NULL;\r
5458     _multi2_dll         = NULL;\r
5459     _streaming_ctrls    = NULL;\r
5460 \r
5461     _idle_count         = 0;\r
5462     _initialized        = false;\r
5463     _reservation_seq_id = 0;\r
5464     _cancel_epg_collect = false;\r
5465 \r
5466     _tunerCount = 0;\r
5467     for (int i = 0; i < ry0::device::MAX_TUNERS; ++i)\r
5468     {\r
5469         _tuners[i] = NULL;\r
5470     }\r
5471 \r
5472     DebugLog0("");\r
5473     DebugLog0("------------------------------------------------------------------------");\r
5474     DebugLog0("iPTd ver %s (rev.%d)", VERSION, REVISION);\r
5475     DebugLog0("initialize...");\r
5476 \r
5477     int result = 0;\r
5478 \r
5479     // 初期化\r
5480     while (true)\r
5481     {\r
5482         bool updated = false;\r
5483 \r
5484         // システムパス設定\r
5485         _system_path = String::stringWithFormat("%s", GetExecutePath());\r
5486         if (_system_path == NULL)\r
5487         {\r
5488             DebugLog0("error: GetExecutePath()");\r
5489             result = -1;\r
5490             break;\r
5491         }\r
5492         _system_path = _system_path->stringByReplacingOccurrencesOfString("iPTd.exe", "");\r
5493         _system_path->retain();\r
5494         DebugLog2("_system_path: %s", _system_path->cString());\r
5495 \r
5496         // プロパティファイルのパス設定\r
5497         _props_path = String::alloc()->initWithFormat("%s%s.iptd.plist", _system_path->cString(), PLIST_PREFIX);\r
5498         if (_props_path == NULL)\r
5499         {\r
5500             DebugLog0("error: set property file path.");\r
5501             result = -1;\r
5502             break;\r
5503         }\r
5504 \r
5505         // プロパティの読み込み\r
5506         _props = Dictionary::alloc()->initWithContentsOfFile(_props_path);\r
5507         if (_props == NULL)\r
5508         {\r
5509             DebugLog1("property file: %s (created)", _props_path->cString());\r
5510             _props = Dictionary::alloc()->initWithCapacity(0);\r
5511         }\r
5512         else\r
5513         {\r
5514             DebugLog1("property file: %s", _props_path->cString());\r
5515         }\r
5516 \r
5517         // ステータスファイルのパス設定\r
5518         _status_path = String::alloc()->initWithFormat("%s%s.iptd.status.plist", _system_path->cString(), PLIST_PREFIX);\r
5519         if (_status_path == NULL)\r
5520         {\r
5521             DebugLog0("error: set status file path.");\r
5522             result = -1;\r
5523             break;\r
5524         }\r
5525 \r
5526         // ステータスの読み込み\r
5527         _status = Dictionary::alloc()->initWithContentsOfFile(_status_path);\r
5528         if (_status == NULL)\r
5529         {\r
5530             DebugLog1("status file: %s (created)", _status_path->cString());\r
5531             _status = Dictionary::alloc()->initWithCapacity(0);\r
5532         }\r
5533         else\r
5534         {\r
5535             DebugLog1("status file: %s", _status_path->cString());\r
5536         }\r
5537 \r
5538         // システム名\r
5539         if (_props->stringForKey(KEY_NAME) == NULL)\r
5540         {\r
5541             _props->setString(DEF_NAME, KEY_NAME);\r
5542             updated = true;\r
5543         }\r
5544 \r
5545         // ホスト名\r
5546         if (_props->stringForKey(KEY_HOSTNAME) == NULL)\r
5547         {\r
5548             _props->setString(DEF_HOSTNAME, KEY_HOSTNAME);\r
5549             updated = true;\r
5550         }\r
5551 \r
5552         // multi2デコード可否判定: multi2.dll が使えるか確認する\r
5553         _multi2_dll = ::LoadLibrary(L"multi2.dll");\r
5554         while (_multi2_dll != NULL)\r
5555         {\r
5556             // b25: 関数アドレス取得\r
5557             ARIB_STD_B25 *(*func_b25)();\r
5558             func_b25 = reinterpret_cast<ARIB_STD_B25 *(*)()>(::GetProcAddress(_multi2_dll, "create_arib_std_b25"));\r
5559             if (func_b25 == NULL)\r
5560             {\r
5561                 // 関数アドレス取得NG\r
5562                 FreeLibrary(_multi2_dll);\r
5563                 _multi2_dll = NULL;\r
5564                 break;\r
5565             }\r
5566 \r
5567             // b25: インスタンス生成\r
5568             ARIB_STD_B25 * _b25;\r
5569             _b25 = func_b25();\r
5570             if (_b25 == NULL)\r
5571             {\r
5572                 // インスタンス生成NG\r
5573                 FreeLibrary(_multi2_dll);\r
5574                 _multi2_dll = NULL;\r
5575                 break;\r
5576             }\r
5577             _b25->release(_b25);\r
5578 \r
5579             // bcas: 関数アドレス取得\r
5580             B_CAS_CARD *(*func_bcas)();\r
5581             func_bcas = reinterpret_cast<B_CAS_CARD *(*)()>(::GetProcAddress(_multi2_dll, "create_b_cas_card"));\r
5582             if (func_bcas == NULL)\r
5583             {\r
5584                 // 関数アドレス取得NG\r
5585                 FreeLibrary(_multi2_dll);\r
5586                 _multi2_dll = NULL;\r
5587                 break;\r
5588             }\r
5589 \r
5590             // bcas: インスタンス生成\r
5591             B_CAS_CARD * _bcas;\r
5592             _bcas = func_bcas();\r
5593             if (_bcas == NULL)\r
5594             {\r
5595                 // インスタンス生成NG\r
5596                 FreeLibrary(_multi2_dll);\r
5597                 _multi2_dll = NULL;\r
5598                 break;\r
5599             }\r
5600             if (_bcas->init(_bcas) != 0)\r
5601             {\r
5602                 // bcas初期化NG\r
5603                 FreeLibrary(_multi2_dll);\r
5604                 _multi2_dll = NULL;\r
5605                 break;\r
5606             }\r
5607             _bcas->release(_bcas);\r
5608 \r
5609             // デコード可\r
5610             break;\r
5611         }\r
5612 \r
5613         // HTTPd ポート\r
5614         if (_props->integerForKey(KEY_HTTP_PORT) == 0)\r
5615         {\r
5616             _props->setInteger(DEF_HTTP_PORT, KEY_HTTP_PORT);\r
5617             updated = true;\r
5618         }\r
5619 \r
5620         // 休止までの時間\r
5621         if ((_props->integerForKey(KEY_SUSPEND_TIME) < DEF_SUSPEND_TIME) ||\r
5622             (_props->integerForKey(KEY_SUSPEND_TIME) >= _props->integerForKey(KEY_FORCED_SUSPEND_TIME)))\r
5623         {\r
5624             _props->setInteger(DEF_SUSPEND_TIME, KEY_SUSPEND_TIME);\r
5625             updated = true;\r
5626         }\r
5627 \r
5628         // 強制休止までの時間\r
5629         if ((_props->integerForKey(KEY_FORCED_SUSPEND_TIME) == 0) ||\r
5630             (_props->integerForKey(KEY_FORCED_SUSPEND_TIME) <= _props->integerForKey(KEY_SUSPEND_TIME)))\r
5631         {\r
5632             _props->setInteger(DEF_FORCED_SUSPEND_TIME, KEY_FORCED_SUSPEND_TIME);\r
5633             updated = true;\r
5634         }\r
5635 \r
5636         // UDPポートの開始番号\r
5637         if (_props->integerForKey(KEY_BEGIN_UDP_PORT) == 0)\r
5638         {\r
5639             _props->setInteger(DEF_BEGIN_UDP_PORT, KEY_BEGIN_UDP_PORT);\r
5640             updated = true;\r
5641         }\r
5642 \r
5643         // EPGを収集する時間\r
5644         if ((_props->stringForKey(KEY_COLLECT_EPG_TIME) == NULL) ||\r
5645             !_props->stringForKey(KEY_COLLECT_EPG_TIME)->isMatch(String::stringWithUTF8String("^\\d\\d:\\d\\d:\\d\\d$")))\r
5646         {\r
5647             _props->setString(DEF_COLLECT_EPG_TIME, KEY_COLLECT_EPG_TIME);\r
5648             updated = true;\r
5649         }\r
5650 \r
5651         // 休止状態抑止アプリリスト\r
5652         if (_props->arrayForKey(KEY_DO_NOT_IN_SUSPEND) == NULL)\r
5653         {\r
5654             Array *apps = Array::arrayWithCapacity(0);\r
5655             apps->addObject(String::stringWithUTF8String("PIXELACORPORATION.*DtvView\\.exe$"));\r
5656             apps->addObject(String::stringWithUTF8String("Common7\\\\IDE\\\\devenv\\.exe$"));\r
5657             apps->addObject(String::stringWithUTF8String("Kodi\\\\Kodi\\.exe$"));\r
5658             _props->setObject(apps, KEY_DO_NOT_IN_SUSPEND);\r
5659             updated = true;\r
5660         }\r
5661 \r
5662         // 録画データ格納先の確認\r
5663         _store_path = _props->stringForKey(KEY_STORE_PATH);\r
5664         if (_store_path ==  NULL)\r
5665         {\r
5666             // プロパティに未設定の場合\r
5667             //   <Public Directory>/Videos を設定\r
5668             const char *public_dir = GetPublicDirectory();\r
5669             if (public_dir == NULL)\r
5670             {\r
5671                 DebugLog0("error: GetPublicDirectory().");\r
5672                 result = -1;\r
5673                 break;\r
5674             }\r
5675             _store_path = String::alloc()->initWithFormat("%s\\Videos", public_dir);\r
5676             _props->setString(_store_path, KEY_STORE_PATH);\r
5677 \r
5678             // 更新フラグ\r
5679             updated = true;\r
5680         }\r
5681         else\r
5682         {\r
5683             _store_path->retain();\r
5684         }\r
5685 \r
5686         // 実際にディレクトリが存在しているか確認\r
5687         FileManager *fm = FileManager::defaultManager();\r
5688         bool isDir = false;\r
5689         if (!fm->fileExistsAtPath(_store_path, &isDir))\r
5690         {\r
5691             isDir = false;\r
5692         }\r
5693         if (!isDir)\r
5694         {\r
5695             DebugLog0("error: \"%s\" is not exists.", _store_path->cString());\r
5696             result = -1;\r
5697             break;\r
5698         }\r
5699 \r
5700         // HLS Preset\r
5701         if (_props->dictionaryForKey(KEY_PRESETS) == NULL)\r
5702         {\r
5703             Dictionary *presets = Dictionary::dictionaryWithCapacity(0);\r
5704             _props->setObject(presets, KEY_PRESETS);\r
5705 \r
5706 // とりあえずこのまま\r
5707 // プリセットは、あとで実装。。。            \r
5708             Array *preset;\r
5709 \r
5710             // "default"\r
5711             preset = STR_ARRAY("-vcodec",         "libx264",\r
5712                               "-b:v",            "768k",\r
5713                               "-s",              "640x360",\r
5714                               "-acodec",         "libfaac",\r
5715                               "-b:a",            "96k",\r
5716                               "-ar",             "44100",\r
5717                               "-flags",          "+loop-global_header",\r
5718                               "-map",            "0",\r
5719                               "-bsf",            "h264_mp4toannexb",\r
5720                               "-f",              "segment",\r
5721                               "-segment_format", "mpegts",\r
5722                               "-segment_time",   "10",\r
5723                               NULL);\r
5724             presets->setObject(preset, KEY_DEFAULT);\r
5725 \r
5726             updated = true;\r
5727         }\r
5728 \r
5729         // キャッシュ(HLSの一時ファイル格納先)パス\r
5730         if (_props->stringForKey(KEY_CACHE_PATH) == NULL)\r
5731         {\r
5732             _props->setString(_store_path->stringByAppendingPathComponent("Cache"), KEY_CACHE_PATH);\r
5733             _mkdir(_props->stringForKey(KEY_CACHE_PATH)->cString());\r
5734             updated = true;\r
5735         }\r
5736         isDir = false;\r
5737         if (!fm->fileExistsAtPath(_props->stringForKey(KEY_CACHE_PATH), &isDir))\r
5738         {\r
5739             isDir = false;\r
5740         }\r
5741         if (!isDir)\r
5742         {\r
5743             DebugLog0("error: \"%s\" is not exists.", _props->stringForKey(KEY_CACHE_PATH)->cString());\r
5744             result = -1;\r
5745             break;\r
5746         }\r
5747 \r
5748         // プロパティファイルを保存\r
5749         if (updated)\r
5750         {\r
5751             DebugLog0("props updated.");\r
5752             if (!_props->writeToFile(_props_path, false))\r
5753             {\r
5754                 DebugLog0("Can't write property file.");\r
5755                 result = -1;\r
5756                 break;\r
5757             }\r
5758         }\r
5759 \r
5760         // プロパティの確認\r
5761         DebugLog0("  Name                : %s", _props->stringForKey(KEY_NAME)->cString());\r
5762         DebugLog0("  Decode Enabled      : %s", (_multi2_dll != NULL) ? "true" : "false");\r
5763         DebugLog0("  HTTP Port           : %d", _props->integerForKey(KEY_HTTP_PORT));\r
5764         DebugLog0("  Suspend Time        : %d min", _props->integerForKey(KEY_SUSPEND_TIME));\r
5765         DebugLog0("  Forced Suspend Time : %d min", _props->integerForKey(KEY_FORCED_SUSPEND_TIME));\r
5766         Array *apps = _props->arrayForKey(KEY_DO_NOT_IN_SUSPEND);\r
5767         if (apps != NULL)\r
5768         {\r
5769             DebugLog1("  Do not in suspend   :");\r
5770             for (uint i = 0; i < apps->count(); ++i)\r
5771             {\r
5772                 DebugLog1("    RegExp[%02d]        : %s", i, ((String *)apps->objectAtIndex(i))->cString());\r
5773             }\r
5774         }\r
5775 \r
5776         // 番組データファイルのパス設定\r
5777         _epgs_path = String::alloc()->initWithFormat("%s%s.iptd.epgs.plist", _system_path->cString(), PLIST_PREFIX);\r
5778         if (_epgs_path == NULL)\r
5779         {\r
5780             DebugLog0("error: set epgs file path.");\r
5781             result = -1;\r
5782             break;\r
5783         }\r
5784 \r
5785         // 番組データの読み込み\r
5786         _epgs = Dictionary::alloc()->initWithContentsOfFile(_epgs_path);\r
5787         if (_epgs == NULL)\r
5788         {\r
5789             DebugLog1("epgs file: %s (created)", _epgs_path->cString());\r
5790             _epgs = Dictionary::alloc()->initWithCapacity(0);\r
5791         }\r
5792         else\r
5793         {\r
5794             DebugLog1("epgs file: %s", _epgs_path->cString());\r
5795 \r
5796             // 過去の番組データは削除\r
5797             removePastEPGs();\r
5798         }\r
5799 \r
5800         // 予約データファイルのパス設定\r
5801         _reservations_path = String::alloc()->initWithFormat("%s%s.iptd.reservations.plist", _system_path->cString(), PLIST_PREFIX);\r
5802         if (_reservations_path == NULL)\r
5803         {\r
5804             DebugLog0("error: set reservations file path.");\r
5805             result = -1;\r
5806             break;\r
5807         }\r
5808 \r
5809         // 予約データの読み込み\r
5810         _reservations = Dictionary::alloc()->initWithContentsOfFile(_reservations_path);\r
5811         if (_reservations == NULL)\r
5812         {\r
5813             DebugLog1("reservations file: %s (created)", _reservations_path->cString());\r
5814             _reservations = Dictionary::alloc()->initWithCapacity(0);\r
5815         }\r
5816         else\r
5817         {\r
5818             DebugLog1("reservations file: %s", _reservations_path->cString());\r
5819 \r
5820             // 予約情報シーケンスID\r
5821             _reservation_seq_id = _reservations->integerForKey(KEY_EPG_LAST_RESV_ID);\r
5822         }\r
5823 \r
5824         // ストリーミング制御情報格納用\r
5825         _streaming_ctrls = Dictionary::alloc()->initWithCapacity(0);\r
5826 \r
5827         // httpdのルートパス\r
5828         String *rootPath = _system_path->stringByAppendingPathComponent("iptd_html");\r
5829         if (!fm->fileExistsAtPath(rootPath, &isDir))\r
5830         {\r
5831             isDir = false;\r
5832         }\r
5833         if (!isDir)\r
5834         {\r
5835             DebugLog0("error: \"%s\" is not exists.", rootPath->cString());\r
5836             result = -1;\r
5837             break;\r
5838         }\r
5839 \r
5840         // httpd開始\r
5841         int port = _props->integerForKey(KEY_HTTP_PORT);\r
5842         _httpd = NET::HTTPDaemon::alloc()->initWithPort(port, 10);\r
5843         _httpd->setRootPath(rootPath);\r
5844         _httpd->setDelegate(this);\r
5845         if (!_httpd->start())\r
5846         {\r
5847             DebugLog0("Can't start httpd.");\r
5848             result = -1;\r
5849             break;\r
5850         }\r
5851 \r
5852         // 再開タイマ起動\r
5853         _timer_restart = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_RESTART, false);\r
5854         _timer_restart->fire();\r
5855 \r
5856         break;\r
5857     }\r
5858 \r
5859     // 初期化エラーがなければイベント待ちへ\r
5860     if (result == 0)\r
5861     {\r
5862         result = Application::start();\r
5863 \r
5864     DebugLog0("will terminate. 00");\r
5865 \r
5866         // httpd終了待ち\r
5867         _httpd->stop();\r
5868     }\r
5869 \r
5870     DebugLog0("will terminate. 01");\r
5871 \r
5872     RaymLock(this);\r
5873     _cancel_epg_collect = true;\r
5874     RaymUnlock(this);\r
5875 \r
5876     // タイマ停止\r
5877     if ((_timer_restart != NULL) && _timer_restart->valid())\r
5878     {\r
5879         _timer_restart->invalidate();\r
5880     }\r
5881     if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
5882     {\r
5883         _timer_epg_s->invalidate();\r
5884     }\r
5885     if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
5886     {\r
5887         _timer_epg_t->invalidate();\r
5888     }\r
5889     if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
5890     {\r
5891         _timer_periodic->invalidate();\r
5892     }\r
5893     if ((_timer_periodic_2 != NULL) && _timer_periodic_2->valid())\r
5894     {\r
5895         _timer_periodic_2->invalidate();\r
5896     }\r
5897 \r
5898     // チューナ解放\r
5899     for (int i = 0; i < _tunerCount; ++i)\r
5900     {\r
5901         if (_tuners[i] != NULL)\r
5902         {\r
5903             delete _tuners[i];\r
5904             _tuners[i] = NULL;\r
5905         }\r
5906     }\r
5907 \r
5908     // スケジュールリセット\r
5909     resetWakeSchedule();\r
5910 \r
5911     // 解放\r
5912     RELEASE(_timer_restart);\r
5913     RELEASE(_timer_epg_s);\r
5914     RELEASE(_timer_epg_t);\r
5915     RELEASE(_timer_periodic);\r
5916     RELEASE(_timer_periodic_2);\r
5917     RELEASE(_streaming_ctrls);\r
5918     RELEASE(_httpd);\r
5919     RELEASE(_system_path);\r
5920     RELEASE(_props_path);\r
5921     RELEASE(_props);\r
5922     RELEASE(_status_path);\r
5923     RELEASE(_status);\r
5924     RELEASE(_epgs_path);\r
5925     RELEASE(_epgs);\r
5926     RELEASE(_store_path);\r
5927     RELEASE(_reservations_path);\r
5928     RELEASE(_reservations);\r
5929 \r
5930     if (_multi2_dll != NULL)\r
5931     {\r
5932         FreeLibrary(_multi2_dll);\r
5933     }\r
5934 \r
5935     pool->release();\r
5936 \r
5937     // 終了\r
5938     DebugLog0("finished.");\r
5939 \r
5940 #ifdef RAYM_MEMORY_CHECK\r
5941     DebugLog0("global_raym_count_ = %d", Raym::global_raym_count_);\r
5942 #endif\r
5943 \r
5944     return result;\r
5945 }\r
5946 \r
5947 #ifndef _WIN32\r
5948 #pragma mark '\r
5949 #pragma mark ------- コンストラクタ/デストラクタ -------\r
5950 #endif\r
5951 \r
5952 Controller::Controller()\r
5953 {\r
5954 }\r
5955 \r
5956 Controller::~Controller()\r
5957 {\r
5958 }\r
5959 \r
5960 Controller *Controller::alloc()\r
5961 {\r
5962     return new Controller();\r
5963 }\r
5964 \r
5965 #ifndef _WIN32\r
5966 #pragma mark '\r
5967 #pragma mark ------- その他 -------\r
5968 #endif\r
5969 \r
5970 //\r
5971 // HH:MM:SS 形式の文字列から time_t に変換\r
5972 // 現在時刻が指定文字列の時刻以前の場合、当日の時刻。指定文字列の時刻よりも過ぎている場合、翌日の時刻を返す。\r
5973 //\r
5974 void Controller::getTimeWithString(String *str, time_t *time_var)\r
5975 {\r
5976     if ((str != NULL) && str->isMatch(String::stringWithUTF8String("^\\d\\d:\\d\\d:\\d\\d$")) && (time_var != NULL))\r
5977     {\r
5978         // 時:分:秒 を int型に分解\r
5979         std::string time_str = str->cString();\r
5980         int hour = atoi(time_str.substr(0, 2).c_str());\r
5981         int min  = atoi(time_str.substr(3, 2).c_str());\r
5982         int sec  = atoi(time_str.substr(6, 2).c_str());\r
5983         DebugLog2("%02d:%02d:%02d", hour, min, sec);\r
5984 \r
5985         // 現在時刻取得\r
5986         time_t now = time(NULL);\r
5987         TM now_tm;\r
5988         if (localtime_s(&now_tm, &now) == 0)\r
5989         {\r
5990             int now_sec = now_tm.tm_hour * 3600 + now_tm.tm_min * 60 + now_tm.tm_sec;\r
5991             int col_sec = hour * 3600 + min * 3600 + sec;\r
5992             if (now_sec > col_sec)\r
5993             {\r
5994                 ++now_tm.tm_mday;\r
5995             }\r
5996             now_tm.tm_hour = hour;\r
5997             now_tm.tm_min  = min;\r
5998             now_tm.tm_sec  = sec;\r
5999 \r
6000             *time_var = mktime(&now_tm);\r
6001         }\r
6002     }\r
6003 }\r
6004 \r
6005 void Controller::getTimeWithEPG(Dictionary *epg, time_t *start, time_t *end)\r
6006 {\r
6007     if ((epg == NULL) || (start == NULL) || (end == NULL))\r
6008     {\r
6009         return;\r
6010     }\r
6011     String *date = epg->stringForKey(KEY_EPG_DATE);\r
6012     String *st   = epg->stringForKey(KEY_EPG_START);\r
6013     String *ed   = epg->stringForKey(KEY_EPG_END);\r
6014     if ((date == NULL) || (st == NULL) || (ed == NULL))\r
6015     {\r
6016         return;\r
6017     }\r
6018 \r
6019     std::string dateStr = date->cString();\r
6020     std::string stStr = st->cString();\r
6021     std::string edStr = ed->cString();\r
6022     TM tm_start;\r
6023     tm_start.tm_year = atoi(dateStr.substr(0, 4).c_str()) - 1900;\r
6024     tm_start.tm_mon  = atoi(dateStr.substr(5, 2).c_str()) - 1;\r
6025     tm_start.tm_mday = atoi(dateStr.substr(8, 2).c_str());\r
6026     tm_start.tm_hour = atoi(stStr.substr(0, 2).c_str());\r
6027     tm_start.tm_min  = atoi(stStr.substr(3, 2).c_str());\r
6028     tm_start.tm_sec  = atoi(stStr.substr(6, 2).c_str());\r
6029 \r
6030     TM tm_end;\r
6031     tm_end.tm_year = atoi(dateStr.substr(0, 4).c_str()) - 1900;\r
6032     tm_end.tm_mon  = atoi(dateStr.substr(5, 2).c_str()) - 1;\r
6033     tm_end.tm_mday = atoi(dateStr.substr(8, 2).c_str());\r
6034     tm_end.tm_hour = atoi(edStr.substr(0, 2).c_str());\r
6035     tm_end.tm_min  = atoi(edStr.substr(3, 2).c_str());\r
6036     tm_end.tm_sec  = atoi(edStr.substr(6, 2).c_str());\r
6037     if (stStr > edStr)\r
6038     {\r
6039         tm_end.tm_mday += 1;\r
6040     }\r
6041     *start = mktime(&tm_start);\r
6042     *end = mktime(&tm_end);\r
6043 }\r
6044 \r
6045 } // iPTd\r
6046 } // ry0\r
6047 \r