577a1ec6a388c82043e19bb03b0ae5f2c04bf582
[openblackhole/openblackhole-enigma2.git] / lib / dvb / epgcache.cpp
1 #include <lib/dvb/epgcache.h>
2 #include <lib/dvb/dvb.h>
3 #include <lib/dvb/lowlevel/eit.h>
4
5 #undef EPG_DEBUG
6
7 #ifdef EPG_DEBUG
8 #include <lib/service/event.h>
9 #endif
10
11 #include <deque>
12 #include <fstream>
13 #include <time.h>
14 #include <unistd.h>  // for usleep
15 #include <sys/vfs.h> // for statfs
16 #include <lib/base/cfile.h>
17 #include <lib/base/eerror.h>
18 #include <lib/base/encoding.h>
19 #include <lib/base/estring.h>
20 #include <lib/dvb/pmt.h>
21 #include <lib/dvb/db.h>
22 #include <lib/python/python.h>
23 #include <lib/base/nconfig.h>
24 #include <dvbsi++/descriptor_tag.h>
25
26 #define HILO(x) (x##_hi << 8 | x##_lo)
27
28 /* Interval between "garbage collect" cycles */
29 #define CLEAN_INTERVAL 60000    //  1 min
30 /* Restart EPG data capture */
31 #define UPDATE_INTERVAL 3600000  // 60 min
32 /* Time to wait after tuning in before EPG data capturing starts */
33 #define ZAP_DELAY 2000          // 2 sec
34
35 struct DescriptorPair
36 {
37         int reference_count;
38         __u8* data;
39
40         DescriptorPair() {}
41         DescriptorPair(int c, __u8* d): reference_count(c), data(d) {}
42 };
43
44 typedef std::tr1::unordered_map<uint32_t, DescriptorPair> DescriptorMap;
45
46 struct eventData
47 {
48         uint8_t rawEITdata[10];
49         uint8_t n_crc;
50         uint8_t type;
51         uint32_t *crc_list;
52         static DescriptorMap descriptors;
53         static uint8_t data[];
54         static unsigned int CacheSize;
55         static bool isCacheCorrupt;
56         eventData(const eit_event_struct* e = NULL, int size = 0, int type = 0, int tsidonid = 0);
57         ~eventData();
58         static void load(FILE *);
59         static void save(FILE *);
60         static void cacheCorrupt(const char* context);
61         const eit_event_struct* get() const;
62         int getEventID() const
63         {
64                 return (rawEITdata[0] << 8) | rawEITdata[1];
65         }
66         time_t getStartTime() const
67         {
68                 return parseDVBtime(&rawEITdata[2]);
69         }
70         int getDuration() const
71         {
72                 return fromBCD(rawEITdata[7])*3600+fromBCD(rawEITdata[8])*60+fromBCD(rawEITdata[9]);
73         }
74 };
75
76 unsigned int eventData::CacheSize = 0;
77 bool eventData::isCacheCorrupt = 0;
78 DescriptorMap eventData::descriptors;
79 uint8_t eventData::data[2 * 4096 + 12];
80 extern const uint32_t crc32_table[256];
81
82 const eServiceReference &handleGroup(const eServiceReference &ref)
83 {
84         if (ref.flags & eServiceReference::isGroup)
85         {
86                 ePtr<eDVBResourceManager> res;
87                 if (!eDVBResourceManager::getInstance(res))
88                 {
89                         ePtr<iDVBChannelList> db;
90                         if (!res->getChannelList(db))
91                         {
92                                 eBouquet *bouquet=0;
93                                 if (!db->getBouquet(ref, bouquet))
94                                 {
95                                         std::list<eServiceReference>::iterator it(bouquet->m_services.begin());
96                                         if (it != bouquet->m_services.end())
97                                                 return *it;
98                                 }
99                         }
100                 }
101         }
102         return ref;
103 }
104
105 static uint32_t calculate_crc_hash(const uint8_t *data, int size)
106 {
107         uint32_t crc = 0;
108         for (int i = 0; i < size; ++i)
109                 crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ data[i]) & 0xFF];
110         return crc;
111 }
112
113 eventData::eventData(const eit_event_struct* e, int size, int _type, int tsidonid)
114         :n_crc(0), type(_type & 0xFF), crc_list(NULL)
115 {
116         if (!e)
117                 return; /* Used when loading from file */
118
119         uint32_t descr[65];
120         uint32_t *pdescr=descr;
121
122         uint8_t *data = (uint8_t*)e;
123         int ptr=12;
124         size -= 12;
125
126         while(size > 1)
127         {
128                 uint8_t *descr = data + ptr;
129                 int descr_len = descr[1];
130                 descr_len += 2;
131                 if (size >= descr_len)
132                 {
133                         switch (descr[0])
134                         {
135                                 case EXTENDED_EVENT_DESCRIPTOR:
136                                 case LINKAGE_DESCRIPTOR:
137                                 case COMPONENT_DESCRIPTOR:
138                                 case CONTENT_DESCRIPTOR:
139                                 case PARENTAL_RATING_DESCRIPTOR:
140                                 case PDC_DESCRIPTOR:
141                                 {
142                                         uint32_t crc = calculate_crc_hash(descr, descr_len);
143                                         DescriptorMap::iterator it = descriptors.find(crc);
144                                         if ( it == descriptors.end() )
145                                         {
146                                                 CacheSize+=descr_len;
147                                                 uint8_t *d = new uint8_t[descr_len];
148                                                 memcpy(d, descr, descr_len);
149                                                 descriptors[crc] = DescriptorPair(1, d);
150                                         }
151                                         else
152                                                 ++it->second.reference_count;
153                                         *pdescr++ = crc;
154                                         break;
155                                 }
156                                 case SHORT_EVENT_DESCRIPTOR:
157                                 {
158                                         //parse the data out from the short event descriptor
159                                         //get the country code, which will be used for converting to UTF8
160                                         std::string cc( (const char*)&descr[2], 3);
161                                         std::transform(cc.begin(), cc.end(), cc.begin(), tolower);
162                                         int table = encodingHandler.getCountryCodeDefaultMapping(cc);
163
164                                         int eventNameLen = descr[5];
165                                         int eventTextLen = descr[6 + eventNameLen];
166
167                                         //convert our strings to UTF8
168                                         std::string eventNameUTF8 = convertDVBUTF8((const unsigned char*)&descr[6], eventNameLen, table, tsidonid);
169                                         std::string textUTF8 = convertDVBUTF8((const unsigned char*)&descr[7 + eventNameLen], eventTextLen, table, tsidonid);
170                                         unsigned int eventNameUTF8len = eventNameUTF8.length();
171                                         unsigned int textUTF8len = textUTF8.length();
172
173                                         //Rebuild the short event descriptor with UTF-8 strings
174
175                                         //Save the title first
176                                         if( eventNameUTF8len > 0 ) //only store the data if there is something to store
177                                         {
178                                                 /*this will actually cause us to save some memory
179                                                  previously some descriptors didnt match because there text was different and titles the same.
180                                                  Now that we store them seperatly we can save some space on title data some rough calculation show anywhere from 20 - 40% savings
181                                                 */
182                                                 eventNameUTF8len = truncateUTF8(eventNameUTF8, 255 - 6);
183                                                 int title_len = 6 + eventNameUTF8len;
184                                                 uint8_t *title_data = new uint8_t[title_len + 2];
185                                                 title_data[0] = SHORT_EVENT_DESCRIPTOR;
186                                                 title_data[1] = title_len;
187                                                 title_data[2] = descr[2];
188                                                 title_data[3] = descr[3];
189                                                 title_data[4] = descr[4];
190                                                 title_data[5] = eventNameUTF8len + 1;
191                                                 title_data[6] = 0x15; //identify event name as UTF-8
192                                                 memcpy(&title_data[7], eventNameUTF8.data(), eventNameUTF8len);
193                                                 title_data[7 + eventNameUTF8len] = 0;
194
195                                                 //Calculate the CRC, based on our new data
196                                                 title_len += 2; //add 2 the length to include the 2 bytes in the header
197                                                 uint32_t title_crc = calculate_crc_hash(title_data, title_len);
198
199                                                 DescriptorMap::iterator it = descriptors.find(title_crc);
200                                                 if ( it == descriptors.end() )
201                                                 {
202                                                         CacheSize += title_len;
203                                                         descriptors[title_crc] = DescriptorPair(1, title_data);
204                                                 }
205                                                 else
206                                                 {
207                                                         ++it->second.reference_count;
208                                                         delete [] title_data;
209                                                 }
210                                                 *pdescr++ = title_crc;
211                                         }
212
213                                         //save the text
214                                         if( textUTF8len > 0 ) //only store the data if there is something to store
215                                         {
216                                                 textUTF8len = truncateUTF8(textUTF8, 255 - 6);
217                                                 int text_len = 6 + textUTF8len;
218                                                 uint8_t *text_data = new uint8_t[text_len + 2];
219                                                 text_data[0] = SHORT_EVENT_DESCRIPTOR;
220                                                 text_data[1] = text_len;
221                                                 text_data[2] = descr[2];
222                                                 text_data[3] = descr[3];
223                                                 text_data[4] = descr[4];
224                                                 text_data[5] = 0;
225                                                 text_data[6] = textUTF8len + 1; //identify text as UTF-8
226                                                 text_data[7] = 0x15; //identify text as UTF-8
227                                                 memcpy(&text_data[8], textUTF8.data(), textUTF8len);
228
229                                                 text_len += 2; //add 2 the length to include the 2 bytes in the header
230                                                 uint32_t text_crc = calculate_crc_hash(text_data, text_len);
231
232                                                 DescriptorMap::iterator it = descriptors.find(text_crc);
233                                                 if ( it == descriptors.end() )
234                                                 {
235                                                         CacheSize += text_len;
236                                                         descriptors[text_crc] = DescriptorPair(1, text_data);
237                                                 }
238                                                 else
239                                                 {
240                                                         ++it->second.reference_count;
241                                                         delete [] text_data;
242                                                 }
243                                                 *pdescr++ = text_crc;
244                                         }
245                                         break;
246                                 }
247                                 default: // do not cache all other descriptors
248                                         break;
249                         }
250                         ptr += descr_len;
251                         size -= descr_len;
252                 }
253                 else
254                         break;
255         }
256         memcpy(rawEITdata, (uint8_t*)e, 10);
257         ASSERT(pdescr <= &descr[65]);
258         n_crc = pdescr - descr;
259         if (n_crc)
260         {
261                 crc_list = new uint32_t[n_crc];
262                 memcpy(crc_list, descr, n_crc * sizeof(uint32_t));
263         }
264         CacheSize += sizeof(*this) + n_crc * sizeof(uint32_t);
265 }
266
267 const eit_event_struct* eventData::get() const
268 {
269         unsigned int pos = 12;
270         memcpy(data, rawEITdata, 10);
271         unsigned int descriptors_length = 0;
272         for (uint8_t i = 0; i < n_crc; ++i)
273         {
274                 DescriptorMap::iterator it = descriptors.find(crc_list[i]);
275                 if (it != descriptors.end())
276                 {
277                         unsigned int b = it->second.data[1] + 2;
278                         if (pos + b < sizeof(data))
279                         {
280                                 memcpy(data + pos, it->second.data, b);
281                                 pos += b;
282                                 descriptors_length += b;
283                         }
284                 }
285                 else
286                         cacheCorrupt("eventData::get");
287         }
288         data[10] = (descriptors_length >> 8) & 0x0F;
289         data[11] = descriptors_length & 0xFF;
290         return (eit_event_struct*)data;
291 }
292
293 eventData::~eventData()
294 {
295         for ( uint8_t i = 0; i < n_crc; ++i )
296         {
297                 DescriptorMap::iterator it = descriptors.find(crc_list[i]);
298                 if ( it != descriptors.end() )
299                 {
300                         DescriptorPair &p = it->second;
301                         if (!--p.reference_count) // no more used descriptor
302                         {
303                                 CacheSize -= it->second.data[1];
304                                 delete [] it->second.data;      // free descriptor memory
305                                 descriptors.erase(it);  // remove entry from descriptor map
306                         }
307                 }
308                 else
309                 {
310                         cacheCorrupt("eventData::~eventData");
311                 }
312         }
313         delete [] crc_list;
314         CacheSize -= sizeof(*this) + n_crc * sizeof(uint32_t);
315 }
316
317 void eventData::load(FILE *f)
318 {
319         int size = 0;
320         int id=0;
321         DescriptorPair p;
322         uint8_t header[2];
323         fread(&size, sizeof(int), 1, f);
324         descriptors.rehash(size);
325         while(size)
326         {
327                 fread(&id, sizeof(uint32_t), 1, f);
328                 fread(&p.reference_count, sizeof(int), 1, f);
329                 fread(header, 2, 1, f);
330                 int bytes = header[1]+2;
331                 p.data = new uint8_t[bytes];
332                 p.data[0] = header[0];
333                 p.data[1] = header[1];
334                 fread(p.data+2, bytes-2, 1, f);
335                 descriptors[id] = p;
336                 --size;
337         }
338 }
339
340 void eventData::save(FILE *f)
341 {
342         if (isCacheCorrupt)
343                 return;
344         int size=descriptors.size();
345         DescriptorMap::iterator it(descriptors.begin());
346         fwrite(&size, sizeof(int), 1, f);
347         while(size)
348         {
349                 fwrite(&it->first, sizeof(uint32_t), 1, f);
350                 fwrite(&it->second.reference_count, sizeof(int), 1, f);
351                 fwrite(it->second.data, it->second.data[1]+2, 1, f);
352                 ++it;
353                 --size;
354         }
355 }
356
357 void eventData::cacheCorrupt(const char* context)
358 {
359
360         eDebug("[eventData] EPG Cache is corrupt (%s), you should restart Enigma!", context);
361         if (!isCacheCorrupt)
362         {
363                 isCacheCorrupt = true;
364                 if (!eEPGCache::instance->m_filename.empty())
365                         unlink(eEPGCache::instance->m_filename.c_str()); // Remove corrupt EPG data
366         }
367 }
368
369
370 eEPGCache* eEPGCache::instance;
371 static pthread_mutex_t cache_lock =
372         PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
373 static pthread_mutex_t channel_map_lock =
374         PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
375
376 DEFINE_REF(eEPGCache)
377
378 eEPGCache::eEPGCache()
379         :messages(this,1), cleanTimer(eTimer::create(this)), m_running(false)
380 {
381         eDebug("[eEPGCache] Initialized EPGCache (wait for setCacheFile call now)");
382
383         enabledSources = 0;
384         historySeconds = 0;
385
386         CONNECT(messages.recv_msg, eEPGCache::gotMessage);
387         CONNECT(eDVBLocalTimeHandler::getInstance()->m_timeUpdated, eEPGCache::timeUpdated);
388         CONNECT(cleanTimer->timeout, eEPGCache::cleanLoop);
389
390         std::ifstream onid_file;
391         onid_file.open("/etc/enigma2/blacklist.onid");
392         int tmp_onid;
393
394         while (onid_file >> std::hex >>tmp_onid)
395                  onid_blacklist.insert(onid_blacklist.end(),1,tmp_onid);
396         onid_file.close();
397
398         ePtr<eDVBResourceManager> res_mgr;
399         eDVBResourceManager::getInstance(res_mgr);
400         if (!res_mgr)
401                 eDebug("[eEPGCache] no resource manager !!!!!!!");
402         else
403                 res_mgr->connectChannelAdded(slot(*this,&eEPGCache::DVBChannelAdded), m_chanAddedConn);
404
405         instance=this;
406 }
407
408 void eEPGCache::setCacheFile(const char *path)
409 {
410         bool inited = !m_filename.empty();
411         m_filename = path;
412         if (!inited)
413         {
414                 eDebug("[eEPGCache] setCacheFile read/write epg data from/to '%s'", m_filename.c_str());
415                 if (eDVBLocalTimeHandler::getInstance()->ready())
416                         timeUpdated();
417         }
418 }
419
420 void eEPGCache::timeUpdated()
421 {
422         if (!m_filename.empty())
423         {
424                 if (!m_running)
425                 {
426                         eDebug("[eEPGCache] time updated.. start EPG Mainloop");
427                         run();
428                         m_running = true;
429                         singleLock s(channel_map_lock);
430                         for (ChannelMap::const_iterator it = m_knownChannels.begin();
431                                 it != m_knownChannels.end(); ++it)
432                         {
433                                 if (it->second->state == -1) {
434                                         it->second->state=0;
435                                         messages.send(Message(Message::startChannel, it->first));
436                                 }
437                         }
438                 } else
439                         messages.send(Message(Message::timeChanged));
440         }
441         else
442                 eDebug("[eEPGCache] time updated.. but cache file not set yet.. dont start epg!!");
443 }
444
445 void eEPGCache::DVBChannelAdded(eDVBChannel *chan)
446 {
447         if ( chan )
448         {
449 //              eDebug("[eEPGCache] add channel %p", chan);
450                 channel_data *data = new channel_data(this);
451                 data->channel = chan;
452                 data->prevChannelState = -1;
453 #ifdef ENABLE_PRIVATE_EPG
454                 data->m_PrivatePid = -1;
455 #endif
456 #ifdef ENABLE_MHW_EPG
457                 data->m_mhw2_channel_pid = 0x231; // defaults for astra 19.2 D+
458                 data->m_mhw2_title_pid = 0x234; // defaults for astra 19.2 D+
459                 data->m_mhw2_summary_pid = 0x236; // defaults for astra 19.2 D+
460 #endif
461                 singleLock s(channel_map_lock);
462                 m_knownChannels.insert( std::pair<iDVBChannel*, channel_data* >(chan, data) );
463                 chan->connectStateChange(slot(*this, &eEPGCache::DVBChannelStateChanged), data->m_stateChangedConn);
464         }
465 }
466
467 void eEPGCache::DVBChannelRunning(iDVBChannel *chan)
468 {
469         ChannelMap::const_iterator it =
470                 m_knownChannels.find(chan);
471         if ( it == m_knownChannels.end() )
472                 eDebug("[eEPGCache] will start non existing channel %p !!!", chan);
473         else
474         {
475                 channel_data &data = *it->second;
476                 ePtr<eDVBResourceManager> res_mgr;
477                 if ( eDVBResourceManager::getInstance( res_mgr ) )
478                         eDebug("[eEPGCache] no res manager!!");
479                 else
480                 {
481                         ePtr<iDVBDemux> demux;
482                         if ( data.channel->getDemux(demux, 0) )
483                         {
484                                 eDebug("[eEPGCache] no demux!!");
485                                 return;
486                         }
487                         else
488                         {
489                                 RESULT res = demux->createSectionReader( this, data.m_NowNextReader );
490                                 if ( res )
491                                 {
492                                         eDebug("[eEPGCache] couldnt initialize nownext reader!!");
493                                         return;
494                                 }
495
496                                 res = demux->createSectionReader( this, data.m_ScheduleReader );
497                                 if ( res )
498                                 {
499                                         eDebug("[eEPGCache] couldnt initialize schedule reader!!");
500                                         return;
501                                 }
502
503                                 res = demux->createSectionReader( this, data.m_ScheduleOtherReader );
504                                 if ( res )
505                                 {
506                                         eDebug("[eEPGCache] couldnt initialize schedule other reader!!");
507                                         return;
508                                 }
509
510 #ifdef ENABLE_VIRGIN
511                                 res = demux->createSectionReader( this, data.m_VirginNowNextReader );
512                                 if ( res )
513                                 {
514                                         eDebug("[eEPGCache] couldnt initialize virgin nownext reader!!");
515                                         return;
516                                 }
517
518                                 res = demux->createSectionReader( this, data.m_VirginScheduleReader );
519                                 if ( res )
520                                 {
521                                         eDebug("[eEPGCache] couldnt initialize virgin schedule reader!!");
522                                         return;
523                                 }
524 #endif
525 #ifdef ENABLE_NETMED
526                                 res = demux->createSectionReader( this, data.m_NetmedScheduleReader );
527                                 if ( res )
528                                 {
529                                         eDebug("[eEPGCache] couldnt initialize netmed schedule reader!!");
530                                         return;
531                                 }
532
533                                 res = demux->createSectionReader( this, data.m_NetmedScheduleOtherReader );
534                                 if ( res )
535                                 {
536                                         eDebug("[eEPGCache] couldnt initialize netmed schedule other reader!!");
537                                         return;
538                                 }
539 #endif
540                                 res = demux->createSectionReader( this, data.m_ViasatReader );
541                                 if ( res )
542                                 {
543                                         eDebug("[eEPGCache] couldnt initialize viasat reader!!");
544                                         return;
545                                 }
546 #ifdef ENABLE_PRIVATE_EPG
547                                 res = demux->createSectionReader( this, data.m_PrivateReader );
548                                 if ( res )
549                                 {
550                                         eDebug("[eEPGCache] couldnt initialize private reader!!");
551                                         return;
552                                 }
553 #endif
554 #ifdef ENABLE_MHW_EPG
555                                 res = demux->createSectionReader( this, data.m_MHWReader );
556                                 if ( res )
557                                 {
558                                         eDebug("[eEPGCache] couldnt initialize mhw reader!!");
559                                         return;
560                                 }
561                                 res = demux->createSectionReader( this, data.m_MHWReader2 );
562                                 if ( res )
563                                 {
564                                         eDebug("[eEPGCache] couldnt initialize mhw reader!!");
565                                         return;
566                                 }
567 #endif
568 #if ENABLE_FREESAT
569                                 res = demux->createSectionReader( this, data.m_FreeSatScheduleOtherReader );
570                                 if ( res )
571                                 {
572                                         eDebug("[eEPGCache] couldnt initialize FreeSat reader!!");
573                                         return;
574                                 }
575                                 res = demux->createSectionReader( this, data.m_FreeSatScheduleOtherReader2 );
576                                 if ( res )
577                                 {
578                                         eDebug("[eEPGCache] couldnt initialize FreeSat reader 2!!");
579                                         return;
580                                 }
581 #endif
582                                 if (m_running)
583                                 {
584                                         data.state = 0;
585                                         messages.send(Message(Message::startChannel, chan));
586                                         // -> gotMessage -> changedService
587                                 }
588                                 else
589                                         data.state=-1;
590                         }
591                 }
592         }
593 }
594
595 void eEPGCache::DVBChannelStateChanged(iDVBChannel *chan)
596 {
597         ChannelMap::iterator it =
598                 m_knownChannels.find(chan);
599         if ( it != m_knownChannels.end() )
600         {
601                 int state=0;
602                 chan->getState(state);
603                 if ( it->second->prevChannelState != state )
604                 {
605                         switch (state)
606                         {
607                                 case iDVBChannel::state_ok:
608                                 {
609                                         eDebug("[eEPGCache] channel %p running", chan);
610                                         DVBChannelRunning(chan);
611                                         break;
612                                 }
613                                 case iDVBChannel::state_release:
614                                 {
615                                         eDebug("[eEPGCache] remove channel %p", chan);
616                                         if (it->second->state >= 0)
617                                                 messages.send(Message(Message::leaveChannel, chan));
618                                         channel_data* cd = it->second;
619                                         pthread_mutex_lock(&cd->channel_active);
620                                         {
621                                                 singleLock s(channel_map_lock);
622                                                 m_knownChannels.erase(it);
623                                         }
624                                         pthread_mutex_unlock(&cd->channel_active);
625                                         delete cd;
626                                         // -> gotMessage -> abortEPG
627                                         return;
628                                 }
629                                 default: // ignore all other events
630                                         return;
631                         }
632                         if (it->second)
633                                 it->second->prevChannelState = state;
634                 }
635         }
636 }
637
638 bool eEPGCache::FixOverlapping(EventCacheItem &servicemap, time_t TM, int duration, const timeMap::iterator &tm_it, const uniqueEPGKey &service)
639 {
640         bool ret = false;
641         timeMap::iterator tmp = tm_it;
642         while ((tmp->first + tmp->second->getDuration() - 300) > TM)
643         {
644                 if(tmp->first != TM
645 #ifdef ENABLE_PRIVATE_EPG
646                         && tmp->second->type != PRIVATE
647 #endif
648 #ifdef ENABLE_MHW_EPG
649                         && tmp->second->type != MHW
650 #endif
651                         )
652                 {
653                         uint16_t event_id = tmp->second->getEventID();
654                         servicemap.byEvent.erase(event_id);
655 #ifdef EPG_DEBUG
656                         Event evt((uint8_t*)tmp->second->get());
657                         eServiceEvent event;
658                         event.parseFrom(&evt, service.sid<<16|service.onid);
659                         eDebug("[eEPGCache] (1)erase no more used event %04x %d\n%s %s\n%s",
660                                 service.sid, event_id,
661                                 event.getBeginTimeString().c_str(),
662                                 event.getEventName().c_str(),
663                                 event.getExtendedDescription().c_str());
664 #endif
665                         delete tmp->second;
666                         if (tmp == servicemap.byTime.begin())
667                         {
668                                 servicemap.byTime.erase(tmp);
669                                 break;
670                         }
671                         else
672                                 servicemap.byTime.erase(tmp--);
673                         ret = true;
674                 }
675                 else
676                 {
677                         if (tmp == servicemap.byTime.begin())
678                                 break;
679                         --tmp;
680                 }
681         }
682
683         tmp = tm_it;
684         while(tmp->first < (TM+duration-300))
685         {
686                 if (tmp->first != TM && tmp->second->type != PRIVATE)
687                 {
688                         uint16_t event_id = tmp->second->getEventID();
689                         servicemap.byEvent.erase(event_id);
690 #ifdef EPG_DEBUG
691                         Event evt((uint8_t*)tmp->second->get());
692                         eServiceEvent event;
693                         event.parseFrom(&evt, service.sid<<16|service.onid);
694                         eDebug("[eEPGCache] (2)erase no more used event %04x %d\n%s %s\n%s",
695                                 service.sid, event_id,
696                                 event.getBeginTimeString().c_str(),
697                                 event.getEventName().c_str(),
698                                 event.getExtendedDescription().c_str());
699 #endif
700                         delete tmp->second;
701                         servicemap.byTime.erase(tmp++);
702                         ret = true;
703                 }
704                 else
705                         ++tmp;
706                 if (tmp == servicemap.byTime.end())
707                         break;
708         }
709         return ret;
710 }
711
712 void eEPGCache::sectionRead(const uint8_t *data, int source, channel_data *channel)
713 {
714         const eit_t *eit = (const eit_t*) data;
715
716         int len = HILO(eit->section_length) - 1;
717         int ptr = EIT_SIZE;
718         if ( ptr >= len )
719                 return;
720
721 #if 0
722                 /*
723                  * disable for now, as this hack breaks EIT parsing for
724                  * services with a low segment_last_table_id
725                  *
726                  * Multichoice should be the exception, not the rule...
727                  */
728
729         // This fixed the EPG on the Multichoice irdeto systems
730         // the EIT packet is non-compliant.. their EIT packet stinks
731         if ( data[ptr-1] < 0x40 )
732                 --ptr;
733 #endif
734
735         int onid = HILO(eit->original_network_id);
736         int tsid  = HILO(eit->transport_stream_id);
737
738         // Cablecom HACK .. tsid / onid in eit data are incorrect.. so we use
739         // it from running channel (just for current transport stream eit data)
740         /*
741          * Make an exception for BEV (onid 0x100, 0x101), which doesn't use
742          * SCHEDULE_OTHER. As a result SCHEDULE will contain data for different tsid's,
743          * so we should not replace it with the current tsid.
744          */
745         bool use_transponder_chid = onid != 0x101 && onid != 0x100 && (source == SCHEDULE || (source == NOWNEXT && data[0] == 0x4E));
746
747         if (use_transponder_chid && channel)
748         {
749                 eDVBChannelID chid = channel->channel->getChannelID();
750
751                 onid = chid.original_network_id.get();
752                 tsid = chid.transport_stream_id.get();
753         }
754         uniqueEPGKey service( HILO(eit->service_id), onid, tsid);
755
756         eit_event_struct* eit_event = (eit_event_struct*) (data+ptr);
757         int eit_event_size;
758         int duration;
759
760         time_t TM = parseDVBtime((const uint8_t*)eit_event + 2);
761         time_t now = ::time(0);
762
763         if ( TM != 3599 && TM > -1 && channel)
764                 channel->haveData |= source;
765
766         singleLock s(cache_lock);
767         // hier wird immer eine eventMap zurck gegeben.. entweder eine vorhandene..
768         // oder eine durch [] erzeugte
769         EventCacheItem &servicemap = eventDB[service];
770         eventMap::iterator prevEventIt = servicemap.byEvent.end();
771         timeMap::iterator prevTimeIt = servicemap.byTime.end();
772
773         while (ptr<len)
774         {
775                 uint16_t event_hash;
776                 eit_event_size = HILO(eit_event->descriptors_loop_length)+EIT_LOOP_SIZE;
777
778                 duration = fromBCD(eit_event->duration_1)*3600+fromBCD(eit_event->duration_2)*60+fromBCD(eit_event->duration_3);
779                 TM = parseDVBtime((const uint8_t*)eit_event + 2, &event_hash);
780
781                 std::vector<int>::iterator m_it=find(onid_blacklist.begin(),onid_blacklist.end(),onid);
782                 if (m_it != onid_blacklist.end())
783                         goto next;
784
785                 if ( (TM != 3599) &&            // NVOD Service
786                      (now <= (TM+duration)) &&  // skip old events
787                      (TM < (now+28*24*60*60)) &&        // no more than 4 weeks in future
788                      ( (onid != 1714) || (duration != (24*3600-1)) )    // PlatformaHD invalid event
789                    )
790                 {
791                         uint16_t event_id = HILO(eit_event->event_id);
792                         eventData *evt = 0;
793                         int ev_erase_count = 0;
794                         int tm_erase_count = 0;
795
796                         if (event_id == 0) {
797                                 // hack for some polsat services on 13.0E..... but this also replaces other valid event_ids with value 0..
798                                 // but we dont care about it...
799                                 event_id = event_hash;
800                                 eit_event->event_id_hi = event_hash >> 8;
801                                 eit_event->event_id_lo = event_hash & 0xFF;
802                         }
803
804                         // search in eventmap
805                         eventMap::iterator ev_it =
806                                 servicemap.byEvent.find(event_id);
807
808 //                      eDebug("[eEPGCache] event_id is %d sid is %04x", event_id, service.sid);
809
810                         // entry with this event_id is already exist ?
811                         if ( ev_it != servicemap.byEvent.end() )
812                         {
813                                 if ( source > ev_it->second->type )  // update needed ?
814                                         goto next; // when not.. then skip this entry
815
816                                 // search this event in timemap
817                                 timeMap::iterator tm_it_tmp =
818                                         servicemap.byTime.find(ev_it->second->getStartTime());
819
820                                 if ( tm_it_tmp != servicemap.byTime.end() )
821                                 {
822                                         if ( tm_it_tmp->first == TM ) // just update eventdata
823                                         {
824                                                 // exempt memory
825                                                 eventData *tmp = ev_it->second;
826                                                 ev_it->second = tm_it_tmp->second =
827                                                         new eventData(eit_event, eit_event_size, source, (tsid<<16)|onid);
828                                                 if (FixOverlapping(servicemap, TM, duration, tm_it_tmp, service))
829                                                 {
830                                                         prevEventIt = servicemap.byEvent.end();
831                                                         prevTimeIt = servicemap.byTime.end();
832                                                 }
833                                                 delete tmp;
834                                                 goto next;
835                                         }
836                                         else  // event has new event begin time
837                                         {
838                                                 tm_erase_count++;
839                                                 // delete the found record from timemap
840                                                 servicemap.byTime.erase(tm_it_tmp);
841                                                 prevTimeIt = servicemap.byTime.end();
842                                         }
843                                 }
844                         }
845
846                         // search in timemap, for check of a case if new time has coincided with time of other event
847                         // or event was is not found in eventmap
848                         timeMap::iterator tm_it =
849                                 servicemap.byTime.find(TM);
850
851                         if ( tm_it != servicemap.byTime.end() )
852                         {
853                                 // event with same start time but another event_id...
854                                 if ( source > tm_it->second->type &&
855                                         ev_it == servicemap.byEvent.end() )
856                                         goto next; // when not.. then skip this entry
857
858                                 // search this time in eventmap
859                                 eventMap::iterator ev_it_tmp =
860                                         servicemap.byEvent.find(tm_it->second->getEventID());
861
862                                 if ( ev_it_tmp != servicemap.byEvent.end() )
863                                 {
864                                         ev_erase_count++;
865                                         // delete the found record from eventmap
866                                         servicemap.byEvent.erase(ev_it_tmp);
867                                         prevEventIt = servicemap.byEvent.end();
868                                 }
869                         }
870                         evt = new eventData(eit_event, eit_event_size, source, (tsid<<16)|onid);
871 #ifdef EPG_DEBUG
872                         bool consistencyCheck=true;
873 #endif
874                         if (ev_erase_count > 0 && tm_erase_count > 0) // 2 different pairs have been removed
875                         {
876                                 // exempt memory
877                                 delete ev_it->second;
878                                 delete tm_it->second;
879                                 ev_it->second=evt;
880                                 tm_it->second=evt;
881                         }
882                         else if (ev_erase_count == 0 && tm_erase_count > 0)
883                         {
884                                 // exempt memory
885                                 delete ev_it->second;
886                                 tm_it = prevTimeIt = servicemap.byTime.insert( prevTimeIt, std::pair<const time_t, eventData*>( TM, evt ) );
887                                 ev_it->second = evt;
888                         }
889                         else if (ev_erase_count > 0 && tm_erase_count == 0)
890                         {
891                                 // exempt memory
892                                 delete tm_it->second;
893                                 ev_it = prevEventIt = servicemap.byEvent.insert( prevEventIt, std::pair<const uint16_t, eventData*>( event_id, evt) );
894                                 tm_it->second = evt;
895                         }
896                         else // added new eventData
897                         {
898 #ifdef EPG_DEBUG
899                                 consistencyCheck=false;
900 #endif
901                                 ev_it = prevEventIt = servicemap.byEvent.insert( prevEventIt, std::pair<const uint16_t, eventData*>( event_id, evt) );
902                                 tm_it = prevTimeIt = servicemap.byTime.insert( prevTimeIt, std::pair<const time_t, eventData*>( TM, evt ) );
903                         }
904
905 #ifdef EPG_DEBUG
906                         if ( consistencyCheck )
907                         {
908                                 if ( tm_it->second != evt || ev_it->second != evt )
909                                         eFatal("[eEPGCache] tm_it->second != ev_it->second");
910                                 else if ( tm_it->second->getStartTime() != tm_it->first )
911                                         eFatal("[eEPGCache] event start_time(%d) non equal timemap key(%d)",
912                                                 tm_it->second->getStartTime(), tm_it->first );
913                                 else if ( tm_it->first != TM )
914                                         eFatal("[eEPGCache] timemap key(%d) non equal TM(%d)",
915                                                 tm_it->first, TM);
916                                 else if ( ev_it->second->getEventID() != ev_it->first )
917                                         eFatal("[eEPGCache] event_id (%d) non equal event_map key(%d)",
918                                                 ev_it->second->getEventID(), ev_it->first);
919                                 else if ( ev_it->first != event_id )
920                                         eFatal("[eEPGCache] eventmap key(%d) non equal event_id(%d)",
921                                                 ev_it->first, event_id );
922                         }
923 #endif
924                         if (FixOverlapping(servicemap, TM, duration, tm_it, service))
925                         {
926                                 prevEventIt = servicemap.byEvent.end();
927                                 prevTimeIt = servicemap.byTime.end();
928                         }
929                 }
930 next:
931 #ifdef EPG_DEBUG
932                 if ( servicemap.byEvent.size() != servicemap.byTime.size() )
933                 {
934                         {
935                                 CFile f("/hdd/event_map.txt", "w+");
936                                 int i = 0;
937                                 for (eventMap::iterator it(servicemap.first.begin()); it != servicemap.first.end(); ++it )
938                                 {
939                                         fprintf(f, "%d(key %d) -> time %d, event_id %d, data %p\n",
940                                         i++, (int)it->first, (int)it->second->getStartTime(), (int)it->second->getEventID(), it->second );
941                                 }
942                         }
943                         {
944                                 CFile f("/hdd/time_map.txt", "w+");
945                                 int i = 0;
946                                 for (timeMap::iterator it(servicemap.second.begin()); it != servicemap.second.end(); ++it )
947                                 {
948                                         fprintf(f, "%d(key %d) -> time %d, event_id %d, data %p\n",
949                                                 i++, (int)it->first, (int)it->second->getStartTime(), (int)it->second->getEventID(), it->second );
950                                 }
951                         }
952                         eFatal("[eEPGCache] (1)map sizes not equal :( sid %04x tsid %04x onid %04x size %d size2 %d",
953                                 service.sid, service.tsid, service.onid,
954                                 servicemap.first.size(), servicemap.second.size() );
955                 }
956 #endif
957                 ptr += eit_event_size;
958                 eit_event = (eit_event_struct*)(((uint8_t*)eit_event) + eit_event_size);
959         }
960 }
961
962 void eEPGCache::flushEPG(const uniqueEPGKey & s)
963 {
964         eDebug("[eEPGCache] flushEPG %d", (int)(bool)s);
965         singleLock l(cache_lock);
966         if (s)  // clear only this service
967         {
968                 eventCache::iterator it = eventDB.find(s);
969                 if ( it != eventDB.end() )
970                 {
971                         eventMap &evMap = it->second.byEvent;
972                         timeMap &tmMap = it->second.byTime;
973                         tmMap.clear();
974                         for (eventMap::iterator i = evMap.begin(); i != evMap.end(); ++i)
975                                 delete i->second;
976                         evMap.clear();
977                         eventDB.erase(it);
978
979                         // TODO .. search corresponding channel for removed service and remove this channel from lastupdated map
980 #ifdef ENABLE_PRIVATE_EPG
981                         contentMaps::iterator it =
982                                 content_time_tables.find(s);
983                         if ( it != content_time_tables.end() )
984                         {
985                                 it->second.clear();
986                                 content_time_tables.erase(it);
987                         }
988 #endif
989                 }
990         }
991         else // clear complete EPG Cache
992         {
993                 for (eventCache::iterator it(eventDB.begin());
994                         it != eventDB.end(); ++it)
995                 {
996                         eventMap &evMap = it->second.byEvent;
997                         timeMap &tmMap = it->second.byTime;
998                         for (eventMap::iterator i = evMap.begin(); i != evMap.end(); ++i)
999                                 delete i->second;
1000                         evMap.clear();
1001                         tmMap.clear();
1002                 }
1003                 eventDB.clear();
1004 #ifdef ENABLE_PRIVATE_EPG
1005                 content_time_tables.clear();
1006 #endif
1007                 channelLastUpdated.clear();
1008                 singleLock m(channel_map_lock);
1009                 for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
1010                         it->second->startEPG();
1011         }
1012 }
1013
1014 void eEPGCache::cleanLoop()
1015 {
1016         { /* scope for cache lock */
1017                 time_t now = ::time(0) - historySeconds;
1018                 singleLock s(cache_lock);
1019
1020                 for (eventCache::iterator DBIt = eventDB.begin(); DBIt != eventDB.end(); DBIt++)
1021                 {
1022                         bool updated = false;
1023                         for (timeMap::iterator It = DBIt->second.byTime.begin(); It != DBIt->second.byTime.end() && It->first < now;)
1024                         {
1025                                 if ( now > (It->first+It->second->getDuration()) )  // outdated normal entry (nvod references to)
1026                                 {
1027                                         // remove entry from eventMap
1028                                         eventMap::iterator b(DBIt->second.byEvent.find(It->second->getEventID()));
1029                                         if ( b != DBIt->second.byEvent.end() )
1030                                         {
1031                                                 // release Heap Memory for this entry   (new ....)
1032 //                                              eDebug("[eEPGCache] delete old event (evmap)");
1033                                                 DBIt->second.byEvent.erase(b);
1034                                         }
1035
1036                                         // remove entry from timeMap
1037 //                                      eDebug("[eEPGCache] release heap mem");
1038                                         delete It->second;
1039                                         DBIt->second.byTime.erase(It++);
1040 //                                      eDebug("[eEPGCache] delete old event (timeMap)");
1041                                         updated = true;
1042                                 }
1043                                 else
1044                                         ++It;
1045                         }
1046 #ifdef ENABLE_PRIVATE_EPG
1047                         if ( updated )
1048                         {
1049                                 contentMaps::iterator x =
1050                                         content_time_tables.find( DBIt->first );
1051                                 if ( x != content_time_tables.end() )
1052                                 {
1053                                         timeMap &tmMap = DBIt->second.byTime;
1054                                         for ( contentMap::iterator i = x->second.begin(); i != x->second.end(); )
1055                                         {
1056                                                 for ( contentTimeMap::iterator it(i->second.begin());
1057                                                         it != i->second.end(); )
1058                                                 {
1059                                                         if ( tmMap.find(it->second.first) == tmMap.end() )
1060                                                                 i->second.erase(it++);
1061                                                         else
1062                                                                 ++it;
1063                                                 }
1064                                                 if ( i->second.size() )
1065                                                         ++i;
1066                                                 else
1067                                                         x->second.erase(i++);
1068                                         }
1069                                 }
1070                         }
1071 #endif
1072                 }
1073         } /* release lock */
1074         cleanTimer->start(CLEAN_INTERVAL,true);
1075 }
1076
1077 eEPGCache::~eEPGCache()
1078 {
1079         m_running = false;
1080         messages.send(Message::quit);
1081         kill(); // waiting for thread shutdown
1082         singleLock s(cache_lock);
1083         for (eventCache::iterator evIt = eventDB.begin(); evIt != eventDB.end(); evIt++)
1084                 for (eventMap::iterator It = evIt->second.byEvent.begin(); It != evIt->second.byEvent.end(); It++)
1085                         delete It->second;
1086 }
1087
1088 void eEPGCache::gotMessage( const Message &msg )
1089 {
1090         switch (msg.type)
1091         {
1092                 case Message::flush:
1093                         flushEPG(msg.service);
1094                         break;
1095                 case Message::startChannel:
1096                 {
1097                         singleLock s(channel_map_lock);
1098                         ChannelMap::const_iterator channel =
1099                                 m_knownChannels.find(msg.channel);
1100                         if ( channel != m_knownChannels.end() )
1101                                 channel->second->startChannel();
1102                         break;
1103                 }
1104                 case Message::leaveChannel:
1105                 {
1106                         singleLock s(channel_map_lock);
1107                         ChannelMap::const_iterator channel =
1108                                 m_knownChannels.find(msg.channel);
1109                         if ( channel != m_knownChannels.end() )
1110                                 channel->second->abortEPG();
1111                         break;
1112                 }
1113                 case Message::quit:
1114                         quit(0);
1115                         break;
1116 #ifdef ENABLE_PRIVATE_EPG
1117                 case Message::got_private_pid:
1118                 {
1119                         singleLock s(channel_map_lock);
1120                         for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
1121                         {
1122                                 eDVBChannel *channel = (eDVBChannel*) it->first;
1123                                 channel_data *data = it->second;
1124                                 eDVBChannelID chid = channel->getChannelID();
1125                                 if ( chid.transport_stream_id.get() == msg.service.tsid &&
1126                                         chid.original_network_id.get() == msg.service.onid &&
1127                                         data->m_PrivatePid == -1 )
1128                                 {
1129                                         data->m_PrevVersion = -1;
1130                                         data->m_PrivatePid = msg.pid;
1131                                         data->m_PrivateService = msg.service;
1132                                         int onid = chid.original_network_id.get();
1133                                         onid |= 0x80000000;  // we use highest bit as private epg indicator
1134                                         chid.original_network_id = onid;
1135                                         updateMap::iterator It = channelLastUpdated.find( chid );
1136                                         int update = ( It != channelLastUpdated.end() ? ( UPDATE_INTERVAL - ( (::time(0)-It->second) * 1000 ) ) : ZAP_DELAY );
1137                                         if (update < ZAP_DELAY)
1138                                                 update = ZAP_DELAY;
1139                                         data->startPrivateTimer->start(update, 1);
1140                                         if (update >= 60000)
1141                                                 eDebug("[eEPGCache] next private update in %i min", update/60000);
1142                                         else if (update >= 1000)
1143                                                 eDebug("[eEPGCache] next private update in %i sec", update/1000);
1144                                         break;
1145                                 }
1146                         }
1147                         break;
1148                 }
1149 #endif
1150 #ifdef ENABLE_MHW_EPG
1151                 case Message::got_mhw2_channel_pid:
1152                 {
1153                         singleLock s(channel_map_lock);
1154                         for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
1155                         {
1156                                 eDVBChannel *channel = (eDVBChannel*) it->first;
1157                                 channel_data *data = it->second;
1158                                 eDVBChannelID chid = channel->getChannelID();
1159                                 if ( chid.transport_stream_id.get() == msg.service.tsid &&
1160                                         chid.original_network_id.get() == msg.service.onid )
1161                                 {
1162                                         data->m_mhw2_channel_pid = msg.pid;
1163                                         eDebug("[eEPGCache] got mhw2 channel pid %04x", msg.pid);
1164                                         break;
1165                                 }
1166                         }
1167                         break;
1168                 }
1169                 case Message::got_mhw2_title_pid:
1170                 {
1171                         singleLock s(channel_map_lock);
1172                         for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
1173                         {
1174                                 eDVBChannel *channel = (eDVBChannel*) it->first;
1175                                 channel_data *data = it->second;
1176                                 eDVBChannelID chid = channel->getChannelID();
1177                                 if ( chid.transport_stream_id.get() == msg.service.tsid &&
1178                                         chid.original_network_id.get() == msg.service.onid )
1179                                 {
1180                                         data->m_mhw2_title_pid = msg.pid;
1181                                         eDebug("[eEPGCache] got mhw2 title pid %04x", msg.pid);
1182                                         break;
1183                                 }
1184                         }
1185                         break;
1186                 }
1187                 case Message::got_mhw2_summary_pid:
1188                 {
1189                         singleLock s(channel_map_lock);
1190                         for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
1191                         {
1192                                 eDVBChannel *channel = (eDVBChannel*) it->first;
1193                                 channel_data *data = it->second;
1194                                 eDVBChannelID chid = channel->getChannelID();
1195                                 if ( chid.transport_stream_id.get() == msg.service.tsid &&
1196                                         chid.original_network_id.get() == msg.service.onid )
1197                                 {
1198                                         data->m_mhw2_summary_pid = msg.pid;
1199                                         eDebug("[eEPGCache] got mhw2 summary pid %04x", msg.pid);
1200                                         break;
1201                                 }
1202                         }
1203                         break;
1204                 }
1205 #endif
1206                 case Message::timeChanged:
1207                         cleanLoop();
1208                         break;
1209                 default:
1210                         eDebug("[eEPGCache] unhandled EPGCache Message!!");
1211                         break;
1212         }
1213 }
1214
1215 void eEPGCache::thread()
1216 {
1217         hasStarted();
1218         nice(4);
1219         load();
1220         cleanLoop();
1221         runLoop();
1222         save();
1223 }
1224
1225 static const char* EPGDAT_IN_FLASH = "/epg.dat";
1226
1227 void eEPGCache::load()
1228 {
1229         if (m_filename.empty())
1230                 m_filename = "/hdd/epg.dat";
1231         const char* EPGDAT = m_filename.c_str();
1232         std::string filenamex = m_filename + ".loading";
1233         const char* EPGDATX = filenamex.c_str();
1234         FILE *f = fopen(EPGDAT, "rb");
1235         int renameResult;
1236         if (f == NULL)
1237         {
1238                 /* No EPG on harddisk, so try internal flash */
1239                 eDebug("[eEPGCache] %s not found, try /epg.dat", EPGDAT);
1240                 EPGDAT = EPGDAT_IN_FLASH;
1241                 f = fopen(EPGDAT, "rb");
1242                 if (f == NULL)
1243                         return;
1244                 renameResult = -1;
1245         }
1246         else
1247         {
1248                 unlink(EPGDATX);
1249                 renameResult = rename(EPGDAT, EPGDATX);
1250                 if (renameResult) eDebug("[eEPGCache] failed to rename %s", EPGDAT);
1251         }
1252         {
1253                 int size=0;
1254                 int cnt=0;
1255                 unsigned int magic=0;
1256                 unlink(EPGDAT_IN_FLASH);/* Don't keep it around when in flash */
1257                 fread( &magic, sizeof(int), 1, f);
1258                 if (magic != 0x98765432)
1259                 {
1260                         eDebug("[eEPGCache] epg file has incorrect byte order.. dont read it");
1261                         fclose(f);
1262                         return;
1263                 }
1264                 char text1[13];
1265                 fread( text1, 13, 1, f);
1266                 if ( !memcmp( text1, "ENIGMA_EPG_V7", 13) )
1267                 {
1268                         singleLock s(cache_lock);
1269                         fread( &size, sizeof(int), 1, f);
1270                         eventDB.rehash(size); /* Reserve buckets in advance */
1271                         while(size--)
1272                         {
1273                                 uniqueEPGKey key;
1274                                 int size=0;
1275                                 fread( &key, sizeof(uniqueEPGKey), 1, f);
1276                                 fread( &size, sizeof(int), 1, f);
1277                                 EventCacheItem& item = eventDB[key]; /* Constructs new entry */
1278                                 while(size--)
1279                                 {
1280                                         uint8_t len=0;
1281                                         uint8_t type=0;
1282                                         eventData *event=0;
1283                                         fread( &type, sizeof(uint8_t), 1, f);
1284                                         fread( &len, sizeof(uint8_t), 1, f);
1285                                         event = new eventData(0, len, type);
1286                                         event->n_crc = (len-10) / sizeof(uint32_t);
1287                                         fread( event->rawEITdata, 10, 1, f);
1288                                         if (event->n_crc)
1289                                         {
1290                                                 event->crc_list = new uint32_t[event->n_crc];
1291                                                 fread( event->crc_list, sizeof(uint32_t), event->n_crc, f);
1292                                         }
1293                                         eventData::CacheSize += sizeof(eventData) + event->n_crc * sizeof(uint32_t);
1294                                         item.byEvent[event->getEventID()] = event;
1295                                         item.byTime[event->getStartTime()] = event;
1296                                         ++cnt;
1297                                 }
1298                         }
1299                         eventData::load(f);
1300                         eDebug("[eEPGCache] %d events read from %s", cnt, EPGDAT);
1301 #ifdef ENABLE_PRIVATE_EPG
1302                         char text2[11];
1303                         fread( text2, 11, 1, f);
1304                         if ( !memcmp( text2, "PRIVATE_EPG", 11) )
1305                         {
1306                                 size=0;
1307                                 fread( &size, sizeof(int), 1, f);
1308                                 while(size--)
1309                                 {
1310                                         int size=0;
1311                                         uniqueEPGKey key;
1312                                         fread( &key, sizeof(uniqueEPGKey), 1, f);
1313                                         eventMap &evMap = eventDB[key].byEvent;
1314                                         fread( &size, sizeof(int), 1, f);
1315                                         while(size--)
1316                                         {
1317                                                 int size;
1318                                                 int content_id;
1319                                                 fread( &content_id, sizeof(int), 1, f);
1320                                                 fread( &size, sizeof(int), 1, f);
1321                                                 while(size--)
1322                                                 {
1323                                                         time_t time1, time2;
1324                                                         uint16_t event_id;
1325                                                         fread( &time1, sizeof(time_t), 1, f);
1326                                                         fread( &time2, sizeof(time_t), 1, f);
1327                                                         fread( &event_id, sizeof(uint16_t), 1, f);
1328                                                         content_time_tables[key][content_id][time1]=std::pair<time_t, uint16_t>(time2, event_id);
1329                                                         eventMap::iterator it =
1330                                                                 evMap.find(event_id);
1331                                                         if (it != evMap.end())
1332                                                                 it->second->type = PRIVATE;
1333                                                 }
1334                                         }
1335                                 }
1336                         }
1337 #endif // ENABLE_PRIVATE_EPG
1338                 }
1339                 else
1340                         eDebug("[eEPGCache] don't read old epg database");
1341                 posix_fadvise(fileno(f), 0, 0, POSIX_FADV_DONTNEED);
1342                 fclose(f);
1343                 // We got this far, so the EPG file is okay.
1344                 if (renameResult == 0)
1345                 {
1346                         renameResult = rename(EPGDATX, EPGDAT);
1347                         if (renameResult) eDebug("[eEPGCache] failed to rename epg.dat back");
1348                 }
1349         }
1350 }
1351
1352 void eEPGCache::save()
1353 {
1354         const char* EPGDAT = m_filename.c_str();
1355         if (eventData::isCacheCorrupt)
1356                 return;
1357         // only save epg.dat if it's worth the trouble...
1358         if (eventData::CacheSize < 10240)
1359                 return;
1360
1361         /* create empty file */
1362         FILE *f = fopen(EPGDAT, "wb");
1363         if (!f)
1364         {
1365                 eDebug("[eEPGCache] Failed to open %s: %m", EPGDAT);
1366                 EPGDAT = EPGDAT_IN_FLASH;
1367                 f = fopen(EPGDAT, "wb");
1368                 if (!f)
1369                         return;
1370         }
1371
1372         char* buf = realpath(EPGDAT, NULL);
1373         if (!buf)
1374         {
1375                 eDebug("[eEPGCache] realpath to %s failed in save: %m", EPGDAT);
1376                 fclose(f);
1377                 return;
1378         }
1379
1380         eDebug("[eEPGCache] store epg to realpath '%s'", buf);
1381
1382         struct statfs s;
1383         off64_t tmp;
1384         if (statfs(buf, &s) < 0) {
1385                 eDebug("[eEPGCache] statfs %s failed in save: %m", buf);
1386                 fclose(f);
1387                 free(buf);
1388                 return;
1389         }
1390
1391         free(buf);
1392
1393         // check for enough free space on storage
1394         tmp=s.f_bfree;
1395         tmp*=s.f_bsize;
1396         if ( tmp < (eventData::CacheSize*12)/10 ) // 20% overhead
1397         {
1398                 eDebug("[eEPGCache] not enough free space at '%s' %lld bytes available but %u needed", buf, tmp, (eventData::CacheSize*12)/10);
1399                 fclose(f);
1400                 return;
1401         }
1402
1403         int cnt=0;
1404         unsigned int magic = 0x98765432;
1405         fwrite( &magic, sizeof(int), 1, f);
1406         const char *text = "UNFINISHED_V7";
1407         fwrite( text, 13, 1, f );
1408         int size = eventDB.size();
1409         fwrite( &size, sizeof(int), 1, f );
1410         for (eventCache::iterator service_it(eventDB.begin()); service_it != eventDB.end(); ++service_it)
1411         {
1412                 timeMap &timemap = service_it->second.byTime;
1413                 fwrite( &service_it->first, sizeof(uniqueEPGKey), 1, f);
1414                 size = timemap.size();
1415                 fwrite( &size, sizeof(int), 1, f);
1416                 for (timeMap::iterator time_it(timemap.begin()); time_it != timemap.end(); ++time_it)
1417                 {
1418                         uint8_t len = time_it->second->n_crc * sizeof(uint32_t) + 10;
1419                         fwrite( &time_it->second->type, sizeof(uint8_t), 1, f );
1420                         fwrite( &len, sizeof(uint8_t), 1, f);
1421                         fwrite( time_it->second->rawEITdata, 10, 1, f);
1422                         fwrite( time_it->second->crc_list, sizeof(uint32_t), time_it->second->n_crc, f);
1423                         ++cnt;
1424                 }
1425         }
1426         eDebug("[eEPGCache] %d events written to %s", cnt, EPGDAT);
1427         eventData::save(f);
1428 #ifdef ENABLE_PRIVATE_EPG
1429         const char* text3 = "PRIVATE_EPG";
1430         fwrite( text3, 11, 1, f );
1431         size = content_time_tables.size();
1432         fwrite( &size, sizeof(int), 1, f);
1433         for (contentMaps::iterator a = content_time_tables.begin(); a != content_time_tables.end(); ++a)
1434         {
1435                 contentMap &content_time_table = a->second;
1436                 fwrite( &a->first, sizeof(uniqueEPGKey), 1, f);
1437                 int size = content_time_table.size();
1438                 fwrite( &size, sizeof(int), 1, f);
1439                 for (contentMap::iterator i = content_time_table.begin(); i != content_time_table.end(); ++i )
1440                 {
1441                         int size = i->second.size();
1442                         fwrite( &i->first, sizeof(int), 1, f);
1443                         fwrite( &size, sizeof(int), 1, f);
1444                         for ( contentTimeMap::iterator it(i->second.begin());
1445                                 it != i->second.end(); ++it )
1446                         {
1447                                 fwrite( &it->first, sizeof(time_t), 1, f);
1448                                 fwrite( &it->second.first, sizeof(time_t), 1, f);
1449                                 fwrite( &it->second.second, sizeof(uint16_t), 1, f);
1450                         }
1451                 }
1452         }
1453 #endif
1454         // write version string after binary data
1455         // has been written to disk.
1456         fsync(fileno(f));
1457         fseek(f, sizeof(int), SEEK_SET);
1458         fwrite("ENIGMA_EPG_V7", 13, 1, f);
1459         fclose(f);
1460 }
1461
1462 eEPGCache::channel_data::channel_data(eEPGCache *ml)
1463         :cache(ml)
1464         ,abortTimer(eTimer::create(ml)), zapTimer(eTimer::create(ml)), state(-2)
1465         ,isRunning(0), haveData(0)
1466 #ifdef ENABLE_PRIVATE_EPG
1467         ,startPrivateTimer(eTimer::create(ml))
1468 #endif
1469 #ifdef ENABLE_MHW_EPG
1470         ,m_MHWTimeoutTimer(eTimer::create(ml))
1471 #endif
1472 {
1473 #ifdef ENABLE_MHW_EPG
1474         CONNECT(m_MHWTimeoutTimer->timeout, eEPGCache::channel_data::MHWTimeout);
1475 #endif
1476         CONNECT(zapTimer->timeout, eEPGCache::channel_data::startEPG);
1477         CONNECT(abortTimer->timeout, eEPGCache::channel_data::abortNonAvail);
1478 #ifdef ENABLE_PRIVATE_EPG
1479         CONNECT(startPrivateTimer->timeout, eEPGCache::channel_data::startPrivateReader);
1480 #endif
1481         pthread_mutex_init(&channel_active, 0);
1482 }
1483
1484 void eEPGCache::channel_data::finishEPG()
1485 {
1486         if (!isRunning)  // epg ready
1487         {
1488                 eDebug("[eEPGCache] stop caching events(%ld)", ::time(0));
1489                 zapTimer->start(UPDATE_INTERVAL, 1);
1490                 eDebug("[eEPGCache] next update in %i min", UPDATE_INTERVAL / 60000);
1491                 for (unsigned int i=0; i < sizeof(seenSections)/sizeof(tidMap); ++i)
1492                 {
1493                         seenSections[i].clear();
1494                         calcedSections[i].clear();
1495                 }
1496 #ifdef ENABLE_MHW_EPG
1497                 cleanupMHW();
1498 #endif
1499 #ifdef ENABLE_FREESAT
1500                 cleanupFreeSat();
1501 #endif
1502                 singleLock l(cache_lock);
1503                 cache->channelLastUpdated[channel->getChannelID()] = ::time(0);
1504         }
1505 }
1506
1507 void eEPGCache::channel_data::startEPG()
1508 {
1509         eDebug("[eEPGCache] start caching events(%ld)", ::time(0));
1510         state=0;
1511         haveData=0;
1512         for (unsigned int i=0; i < sizeof(seenSections)/sizeof(tidMap); ++i)
1513         {
1514                 seenSections[i].clear();
1515                 calcedSections[i].clear();
1516         }
1517 #ifdef ENABLE_MHW_EPG
1518                 cleanupMHW();
1519 #endif
1520 #ifdef ENABLE_FREESAT
1521                 cleanupFreeSat();
1522 #endif
1523
1524         eDVBSectionFilterMask mask;
1525         memset(&mask, 0, sizeof(mask));
1526
1527 #ifdef ENABLE_MHW_EPG
1528         if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::MHW)
1529         {
1530                 mask.pid = 0xD3;
1531                 mask.data[0] = 0x91;
1532                 mask.mask[0] = 0xFF;
1533                 m_MHWReader->connectRead(slot(*this, &eEPGCache::channel_data::readMHWData), m_MHWConn);
1534                 m_MHWReader->start(mask);
1535                 isRunning |= MHW;
1536                 memcpy(&m_MHWFilterMask, &mask, sizeof(eDVBSectionFilterMask));
1537
1538                 mask.pid = m_mhw2_channel_pid;
1539                 mask.data[0] = 0xC8;
1540                 mask.mask[0] = 0xFF;
1541                 mask.data[1] = 0;
1542                 mask.mask[1] = 0xFF;
1543                 m_MHWReader2->connectRead(slot(*this, &eEPGCache::channel_data::readMHWData2), m_MHWConn2);
1544                 m_MHWReader2->start(mask);
1545                 isRunning |= MHW;
1546                 memcpy(&m_MHWFilterMask2, &mask, sizeof(eDVBSectionFilterMask));
1547                 mask.data[1] = 0;
1548                 mask.mask[1] = 0;
1549                 m_MHWTimeoutet=false;
1550         }
1551 #endif
1552 #ifdef ENABLE_FREESAT
1553         if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::FREESAT_SCHEDULE_OTHER)
1554         {
1555                 mask.pid = 3842;
1556                 mask.flags = eDVBSectionFilterMask::rfCRC;
1557                 mask.data[0] = 0x60;
1558                 mask.mask[0] = 0xFE;
1559                 m_FreeSatScheduleOtherReader->connectRead(slot(*this, &eEPGCache::channel_data::readFreeSatScheduleOtherData), m_FreeSatScheduleOtherConn);
1560                 m_FreeSatScheduleOtherReader->start(mask);
1561
1562                 /*
1563                  * faster pid, available on ITV HD transponder.
1564                  * We rely on the fact that we have either of the two,
1565                  * never both. (both readers share the same data callback
1566                  * and status maps)
1567                  */
1568                 mask.pid = 3003;
1569                 m_FreeSatScheduleOtherReader2->connectRead(slot(*this, &eEPGCache::channel_data::readFreeSatScheduleOtherData), m_FreeSatScheduleOtherConn2);
1570                 m_FreeSatScheduleOtherReader2->start(mask);
1571                 isRunning |= FREESAT_SCHEDULE_OTHER;
1572         }
1573 #endif
1574         mask.pid = 0x12;
1575         mask.flags = eDVBSectionFilterMask::rfCRC;
1576
1577         if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::NOWNEXT)
1578         {
1579                 mask.data[0] = 0x4E;
1580                 mask.mask[0] = 0xFE;
1581                 m_NowNextReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::NOWNEXT), m_NowNextConn);
1582                 m_NowNextReader->start(mask);
1583                 isRunning |= NOWNEXT;
1584         }
1585
1586         if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::SCHEDULE)
1587         {
1588                 mask.data[0] = 0x50;
1589                 mask.mask[0] = 0xF0;
1590                 m_ScheduleReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::SCHEDULE), m_ScheduleConn);
1591                 m_ScheduleReader->start(mask);
1592                 isRunning |= SCHEDULE;
1593         }
1594
1595         if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::SCHEDULE_OTHER)
1596         {
1597                 mask.data[0] = 0x60;
1598                 mask.mask[0] = 0xF0;
1599                 m_ScheduleOtherReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::SCHEDULE_OTHER), m_ScheduleOtherConn);
1600                 m_ScheduleOtherReader->start(mask);
1601                 isRunning |= SCHEDULE_OTHER;
1602         }
1603
1604 #ifdef ENABLE_VIRGIN
1605         if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::VIRGIN_NOWNEXT)
1606         {
1607                 mask.pid = 0x2bc;
1608                 mask.data[0] = 0x4E;
1609                 mask.mask[0] = 0xFE;
1610                 m_VirginNowNextReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::VIRGIN_NOWNEXT), m_VirginNowNextConn);
1611                 m_VirginNowNextReader->start(mask);
1612                 isRunning |= VIRGIN_NOWNEXT;
1613         }
1614
1615         if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::VIRGIN_SCHEDULE)
1616         {
1617                 mask.pid = 0x2bc;
1618                 mask.data[0] = 0x50;
1619                 mask.mask[0] = 0xFE;
1620                 m_VirginScheduleReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::VIRGIN_SCHEDULE), m_VirginScheduleConn);
1621                 m_VirginScheduleReader->start(mask);
1622                 isRunning |= VIRGIN_SCHEDULE;
1623         }
1624 #endif
1625 #ifdef ENABLE_NETMED
1626         if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::NETMED_SCHEDULE)
1627         {
1628                 mask.pid = 0x1388;
1629                 mask.data[0] = 0x50;
1630                 mask.mask[0] = 0xF0;
1631                 m_NetmedScheduleReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::NETMED_SCHEDULE), m_NetmedScheduleConn);
1632                 m_NetmedScheduleReader->start(mask);
1633                 isRunning |= NETMED_SCHEDULE;
1634         }
1635
1636         if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::NETMED_SCHEDULE_OTHER)
1637         {
1638                 mask.pid = 0x1388;
1639                 mask.data[0] = 0x60;
1640                 mask.mask[0] = 0xF0;
1641                 m_NetmedScheduleOtherReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::NETMED_SCHEDULE_OTHER), m_NetmedScheduleOtherConn);
1642                 m_NetmedScheduleOtherReader->start(mask);
1643                 isRunning |= NETMED_SCHEDULE_OTHER;
1644         }
1645 #endif
1646         if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::VIASAT)
1647         {
1648                 mask.pid = 0x39;
1649
1650                 mask.data[0] = 0x40;
1651                 mask.mask[0] = 0x40;
1652                 m_ViasatReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::VIASAT), m_ViasatConn);
1653                 m_ViasatReader->start(mask);
1654                 isRunning |= VIASAT;
1655         }
1656
1657         abortTimer->start(7000,true);
1658 }
1659
1660 void eEPGCache::channel_data::abortNonAvail()
1661 {
1662         if (!state)
1663         {
1664                 if ( !(haveData&NOWNEXT) && (isRunning&NOWNEXT) )
1665                 {
1666                         eDebug("[eEPGCache] abort non avail nownext reading");
1667                         isRunning &= ~NOWNEXT;
1668                         m_NowNextReader->stop();
1669                         m_NowNextConn=0;
1670                 }
1671                 if ( !(haveData&SCHEDULE) && (isRunning&SCHEDULE) )
1672                 {
1673                         eDebug("[eEPGCache] abort non avail schedule reading");
1674                         isRunning &= ~SCHEDULE;
1675                         m_ScheduleReader->stop();
1676                         m_ScheduleConn=0;
1677                 }
1678                 if ( !(haveData&SCHEDULE_OTHER) && (isRunning&SCHEDULE_OTHER) )
1679                 {
1680                         eDebug("[eEPGCache] abort non avail schedule other reading");
1681                         isRunning &= ~SCHEDULE_OTHER;
1682                         m_ScheduleOtherReader->stop();
1683                         m_ScheduleOtherConn=0;
1684                 }
1685 #ifdef ENABLE_VIRGIN
1686                 if ( !(haveData&VIRGIN_NOWNEXT) && (isRunning&VIRGIN_NOWNEXT) )
1687                 {
1688                         eDebug("[eEPGCache] abort non avail virgin nownext reading");
1689                         isRunning &= ~VIRGIN_NOWNEXT;
1690                         m_VirginNowNextReader->stop();
1691                         m_VirginNowNextConn=0;
1692                 }
1693                 if ( !(haveData&VIRGIN_SCHEDULE) && (isRunning&VIRGIN_SCHEDULE) )
1694                 {
1695                         eDebug("[eEPGCache] abort non avail virgin schedule reading");
1696                         isRunning &= ~VIRGIN_SCHEDULE;
1697                         m_VirginScheduleReader->stop();
1698                         m_VirginScheduleConn=0;
1699                 }
1700 #endif
1701 #ifdef ENABLE_NETMED
1702                 if ( !(haveData&NETMED_SCHEDULE) && (isRunning&NETMED_SCHEDULE) )
1703                 {
1704                         eDebug("[eEPGCache] abort non avail netmed schedule reading");
1705                         isRunning &= ~NETMED_SCHEDULE;
1706                         m_NetmedScheduleReader->stop();
1707                         m_NetmedScheduleConn=0;
1708                 }
1709                 if ( !(haveData&NETMED_SCHEDULE_OTHER) && (isRunning&NETMED_SCHEDULE_OTHER) )
1710                 {
1711                         eDebug("[eEPGCache] abort non avail netmed schedule other reading");
1712                         isRunning &= ~NETMED_SCHEDULE_OTHER;
1713                         m_NetmedScheduleOtherReader->stop();
1714                         m_NetmedScheduleOtherConn=0;
1715                 }
1716 #endif
1717 #ifdef ENABLE_FREESAT
1718                 if ( !(haveData&FREESAT_SCHEDULE_OTHER) && (isRunning&FREESAT_SCHEDULE_OTHER) )
1719                 {
1720                         eDebug("[eEPGCache] abort non avail FreeSat schedule_other reading");
1721                         isRunning &= ~FREESAT_SCHEDULE_OTHER;
1722                         m_FreeSatScheduleOtherReader->stop();
1723                         m_FreeSatScheduleOtherReader2->stop();
1724                         m_FreeSatScheduleOtherConn=0;
1725                         m_FreeSatScheduleOtherConn2=0;
1726                         cleanupFreeSat();
1727                 }
1728 #endif
1729                 if ( !(haveData&VIASAT) && (isRunning&VIASAT) )
1730                 {
1731                         eDebug("[eEPGCache] abort non avail viasat reading");
1732                         isRunning &= ~VIASAT;
1733                         m_ViasatReader->stop();
1734                         m_ViasatConn=0;
1735                 }
1736 #ifdef ENABLE_MHW_EPG
1737                 if ( !(haveData&MHW) && (isRunning&MHW) )
1738                 {
1739                         eDebug("[eEPGCache] abort non avail mhw reading");
1740                         isRunning &= ~MHW;
1741                         m_MHWReader->stop();
1742                         m_MHWConn=0;
1743                         m_MHWReader2->stop();
1744                         m_MHWConn2=0;
1745                 }
1746 #endif
1747                 if ( isRunning & VIASAT )
1748                         abortTimer->start(300000, true);
1749                 else if ( isRunning )
1750                         abortTimer->start(90000, true);
1751                 else
1752                 {
1753                         ++state;
1754                         for (unsigned int i=0; i < sizeof(seenSections)/sizeof(tidMap); ++i)
1755                         {
1756                                 seenSections[i].clear();
1757                                 calcedSections[i].clear();
1758                         }
1759 #ifdef ENABLE_MHW_EPG
1760                         cleanupMHW();
1761 #endif
1762 #ifdef ENABLE_FREESAT
1763                         cleanupFreeSat();
1764 #endif
1765                 }
1766         }
1767         ++state;
1768 }
1769
1770 void eEPGCache::channel_data::startChannel()
1771 {
1772         pthread_mutex_lock(&channel_active);
1773         updateMap::iterator It = cache->channelLastUpdated.find( channel->getChannelID() );
1774
1775         int update = ( It != cache->channelLastUpdated.end() ? ( UPDATE_INTERVAL - ( (::time(0)-It->second) * 1000 ) ) : ZAP_DELAY );
1776
1777         if (update < ZAP_DELAY)
1778                 update = ZAP_DELAY;
1779
1780         zapTimer->start(update, 1);
1781         if (update >= 60000)
1782                 eDebug("[eEPGCache] next update in %i min", update/60000);
1783         else if (update >= 1000)
1784                 eDebug("[eEPGCache] next update in %i sec", update/1000);
1785 }
1786
1787 void eEPGCache::channel_data::abortEPG()
1788 {
1789         for (unsigned int i=0; i < sizeof(seenSections)/sizeof(tidMap); ++i)
1790         {
1791                 seenSections[i].clear();
1792                 calcedSections[i].clear();
1793         }
1794 #ifdef ENABLE_MHW_EPG
1795         cleanupMHW();
1796 #endif
1797 #ifdef ENABLE_FREESAT
1798         cleanupFreeSat();
1799 #endif
1800         abortTimer->stop();
1801         zapTimer->stop();
1802         if (isRunning)
1803         {
1804                 eDebug("[eEPGCache] abort caching events !!");
1805                 if (isRunning & SCHEDULE)
1806                 {
1807                         isRunning &= ~SCHEDULE;
1808                         m_ScheduleReader->stop();
1809                         m_ScheduleConn=0;
1810                 }
1811                 if (isRunning & NOWNEXT)
1812                 {
1813                         isRunning &= ~NOWNEXT;
1814                         m_NowNextReader->stop();
1815                         m_NowNextConn=0;
1816                 }
1817                 if (isRunning & SCHEDULE_OTHER)
1818                 {
1819                         isRunning &= ~SCHEDULE_OTHER;
1820                         m_ScheduleOtherReader->stop();
1821                         m_ScheduleOtherConn=0;
1822                 }
1823 #ifdef ENABLE_VIRGIN
1824                 if (isRunning & VIRGIN_NOWNEXT)
1825                 {
1826                         isRunning &= ~VIRGIN_NOWNEXT;
1827                         m_VirginNowNextReader->stop();
1828                         m_VirginNowNextConn=0;
1829                 }
1830                 if (isRunning & VIRGIN_SCHEDULE)
1831                 {
1832                         isRunning &= ~VIRGIN_SCHEDULE;
1833                         m_VirginScheduleReader->stop();
1834                         m_VirginScheduleConn=0;
1835                 }
1836 #endif
1837 #ifdef ENABLE_NETMED
1838                 if (isRunning & NETMED_SCHEDULE)
1839                 {
1840                         isRunning &= ~NETMED_SCHEDULE;
1841                         m_NetmedScheduleReader->stop();
1842                         m_NetmedScheduleConn=0;
1843                 }
1844                 if (isRunning & NETMED_SCHEDULE_OTHER)
1845                 {
1846                         isRunning &= ~NETMED_SCHEDULE_OTHER;
1847                         m_NetmedScheduleOtherReader->stop();
1848                         m_NetmedScheduleOtherConn=0;
1849                 }
1850 #endif
1851 #ifdef ENABLE_FREESAT
1852                 if (isRunning & FREESAT_SCHEDULE_OTHER)
1853                 {
1854                         isRunning &= ~FREESAT_SCHEDULE_OTHER;
1855                         m_FreeSatScheduleOtherReader->stop();
1856                         m_FreeSatScheduleOtherReader2->stop();
1857                         m_FreeSatScheduleOtherConn=0;
1858                         m_FreeSatScheduleOtherConn2=0;
1859                 }
1860 #endif
1861                 if (isRunning & VIASAT)
1862                 {
1863                         isRunning &= ~VIASAT;
1864                         m_ViasatReader->stop();
1865                         m_ViasatConn=0;
1866                 }
1867 #ifdef ENABLE_MHW_EPG
1868                 if (isRunning & MHW)
1869                 {
1870                         isRunning &= ~MHW;
1871                         m_MHWReader->stop();
1872                         m_MHWConn=0;
1873                         m_MHWReader2->stop();
1874                         m_MHWConn2=0;
1875                 }
1876 #endif
1877         }
1878 #ifdef ENABLE_PRIVATE_EPG
1879         if (m_PrivateReader)
1880                 m_PrivateReader->stop();
1881         if (m_PrivateConn)
1882                 m_PrivateConn=0;
1883 #endif
1884         pthread_mutex_unlock(&channel_active);
1885 }
1886
1887 void eEPGCache::channel_data::readData( const uint8_t *data, int source)
1888 {
1889         int map;
1890         iDVBSectionReader *reader = NULL;
1891         switch (source)
1892         {
1893                 case NOWNEXT:
1894                         reader = m_NowNextReader;
1895                         map = 0;
1896                         break;
1897                 case SCHEDULE:
1898                         reader = m_ScheduleReader;
1899                         map = 1;
1900                         break;
1901                 case SCHEDULE_OTHER:
1902                         reader = m_ScheduleOtherReader;
1903                         map = 2;
1904                         break;
1905                 case VIASAT:
1906                         reader = m_ViasatReader;
1907                         map = 3;
1908                         break;
1909 #ifdef ENABLE_NETMED
1910                 case NETMED_SCHEDULE:
1911                         reader = m_NetmedScheduleReader;
1912                         map = 1;
1913                         break;
1914                 case NETMED_SCHEDULE_OTHER:
1915                         reader = m_NetmedScheduleOtherReader;
1916                         map = 2;
1917                         break;
1918 #endif
1919 #ifdef ENABLE_VIRGIN
1920                 case VIRGIN_NOWNEXT:
1921                         reader = m_VirginNowNextReader;
1922                         map = 0;
1923                         break;
1924                 case VIRGIN_SCHEDULE:
1925                         reader = m_VirginScheduleReader;
1926                         map = 1;
1927                         break;
1928 #endif
1929                 default:
1930                         eDebug("[eEPGCache] unknown source");
1931                         return;
1932         }
1933         tidMap &seenSections = this->seenSections[map];
1934         tidMap &calcedSections = this->calcedSections[map];
1935         if ( (state == 1 && calcedSections == seenSections) || state > 1 )
1936         {
1937                 eDebugNoNewLineStart("[eEPGCache] ");
1938                 switch (source)
1939                 {
1940                         case NOWNEXT:
1941                                 m_NowNextConn=0;
1942                                 eDebugNoNewLine("nownext");
1943                                 break;
1944                         case SCHEDULE:
1945                                 m_ScheduleConn=0;
1946                                 eDebugNoNewLine("schedule");
1947                                 break;
1948                         case SCHEDULE_OTHER:
1949                                 m_ScheduleOtherConn=0;
1950                                 eDebugNoNewLine("schedule other");
1951                                 break;
1952                         case VIASAT:
1953                                 m_ViasatConn=0;
1954                                 eDebugNoNewLine("viasat");
1955                                 break;
1956 #ifdef ENABLE_NETMED
1957                         case NETMED_SCHEDULE:
1958                                 m_NetmedScheduleConn=0;
1959                                 eDebugNoNewLine("netmed schedule");
1960                                 break;
1961                         case NETMED_SCHEDULE_OTHER:
1962                                 m_NetmedScheduleOtherConn=0;
1963                                 eDebugNoNewLine("netmed schedule other");
1964                                 break;
1965 #endif
1966 #ifdef ENABLE_VIRGIN
1967                         case VIRGIN_NOWNEXT:
1968                                 m_VirginNowNextConn=0;
1969                                 eDebugNoNewLine("virgin nownext");
1970                                 break;
1971                         case VIRGIN_SCHEDULE:
1972                                 m_VirginScheduleConn=0;
1973                                 eDebugNoNewLine("virgin schedule");
1974                                 break;
1975 #endif
1976                         default: eDebugNoNewLine("unknown");break;
1977                 }
1978                 eDebugNoNewLine(" finished(%ld)\n", ::time(0));
1979                 if ( reader )
1980                         reader->stop();
1981                 isRunning &= ~source;
1982                 if (!isRunning)
1983                         finishEPG();
1984         }
1985         else
1986         {
1987                 eit_t *eit = (eit_t*) data;
1988                 uint32_t sectionNo = data[0] << 24;
1989                 sectionNo |= data[3] << 16;
1990                 sectionNo |= data[4] << 8;
1991                 sectionNo |= eit->section_number;
1992
1993                 tidMap::iterator it =
1994                         seenSections.find(sectionNo);
1995
1996                 if ( it == seenSections.end() )
1997                 {
1998                         seenSections.insert(sectionNo);
1999                         calcedSections.insert(sectionNo);
2000                         uint32_t tmpval = sectionNo & 0xFFFFFF00;
2001                         uint8_t incr = source == NOWNEXT ? 1 : 8;
2002                         for ( int i = 0; i <= eit->last_section_number; i+=incr )
2003                         {
2004                                 if ( i == eit->section_number )
2005                                 {
2006                                         for (int x=i; x <= eit->segment_last_section_number; ++x)
2007                                                 calcedSections.insert(tmpval|(x&0xFF));
2008                                 }
2009                                 else
2010                                         calcedSections.insert(tmpval|(i&0xFF));
2011                         }
2012                         cache->sectionRead(data, source, this);
2013                 }
2014         }
2015 }
2016
2017 #if ENABLE_FREESAT
2018
2019 freesatEITSubtableStatus::freesatEITSubtableStatus(u_char version, uint8_t maxSection) : version(version)
2020 {
2021         initMap(maxSection);
2022 }
2023
2024 void freesatEITSubtableStatus::initMap(uint8_t maxSection)
2025 {
2026         int i, maxSectionIdx = maxSection / 8;
2027         for (i = 0; i < 32; i++)
2028         {
2029                 sectionMap[i] = (i <= maxSectionIdx ? 0x0100 : 0x0000 );
2030         }
2031 }
2032
2033 bool freesatEITSubtableStatus::isSectionPresent(uint8_t sectionNo)
2034 {
2035         uint8_t sectionIdx = sectionNo / 8;
2036         uint8_t bitOffset = sectionNo % 8;
2037
2038         return ((sectionMap[sectionIdx] & (1 << bitOffset)) != 0);
2039 }
2040
2041 bool freesatEITSubtableStatus::isCompleted()
2042 {
2043         uint32_t i = 0;
2044         uint8_t calc;
2045
2046         while ( i < 32 )
2047         {
2048                 calc = sectionMap[i] >> 8;
2049                 if (! calc) return true; // Last segment passed
2050                 if (calc ^ ( sectionMap[i] & 0xFF ) ) // Segment not fully found
2051                         return false;
2052                 i++;
2053         }
2054         return true; // All segments ok
2055 }
2056
2057 void freesatEITSubtableStatus::seen(uint8_t sectionNo, uint8_t maxSegmentSection)
2058 {
2059         uint8_t sectionIdx = sectionNo / 8;
2060         uint8_t bitOffset = sectionNo % 8;
2061         uint8_t maxBitOffset = maxSegmentSection % 8;
2062
2063         sectionMap[sectionIdx] &= 0x00FF; // Clear calc map
2064         sectionMap[sectionIdx] |= ((0x01FF << maxBitOffset) & 0xFF00); // Set calc map
2065         sectionMap[sectionIdx] |= (1 << bitOffset); // Set seen map
2066 }
2067
2068 bool freesatEITSubtableStatus::isVersionChanged(u_char testVersion)
2069 {
2070         return version != testVersion;
2071 }
2072
2073 void freesatEITSubtableStatus::updateVersion(u_char newVersion, uint8_t maxSection)
2074 {
2075         version = newVersion;
2076         initMap(maxSection);
2077 }
2078
2079 void eEPGCache::channel_data::cleanupFreeSat()
2080 {
2081         m_FreeSatSubTableStatus.clear();
2082         m_FreesatTablesToComplete = 0;
2083 }
2084
2085 void eEPGCache::channel_data::readFreeSatScheduleOtherData( const uint8_t *data)
2086 {
2087         eit_t *eit = (eit_t*) data;
2088         uint32_t subtableNo = data[0] << 24; // Table ID
2089         subtableNo |= data[3] << 16; // Service ID Hi
2090         subtableNo |= data[4] << 8; // Service ID Lo
2091
2092         // Check for sub-table version in map
2093         std::map<uint32_t, freesatEITSubtableStatus> &freeSatSubTableStatus = this->m_FreeSatSubTableStatus;
2094         std::map<uint32_t, freesatEITSubtableStatus>::iterator itmap = freeSatSubTableStatus.find(subtableNo);
2095
2096         freesatEITSubtableStatus *fsstatus;
2097         if ( itmap == freeSatSubTableStatus.end() )
2098         {
2099                 // New sub table. Store version.
2100                 //eDebug("[eEPGCache] New subtable (%x) version (%d) now/next (%d) tsid (%x/%x) onid (%x/%x)", subtableNo, eit->version_number, eit->current_next_indicator, eit->transport_stream_id_hi, eit->transport_stream_id_lo, eit->original_network_id_hi, eit->original_network_id_lo);
2101                 fsstatus = new freesatEITSubtableStatus(eit->version_number, eit->last_section_number);
2102                 m_FreesatTablesToComplete++;
2103                 freeSatSubTableStatus.insert(std::pair<uint32_t,freesatEITSubtableStatus>(subtableNo, *fsstatus));
2104         }
2105         else
2106         {
2107                 fsstatus = &itmap->second;
2108                 // Existing subtable. Check version. Should check current / next as well? Seems to always be current for Freesat
2109                 if ( fsstatus->isVersionChanged(eit->version_number) )
2110                 {
2111                         eDebug("[eEPGCache] FS subtable (%x) version changed (%d) now/next (%d)", subtableNo, eit->version_number, eit->current_next_indicator);
2112                         m_FreesatTablesToComplete++;
2113                         fsstatus->updateVersion(eit->version_number, eit->last_section_number);
2114                 }
2115                 else
2116                 {
2117                         if ( fsstatus->isSectionPresent(eit->section_number) )
2118                         {
2119 //                              eDebug("[eEPGCache] DUP FS sub/sec/ver (%x/%d/%d)", subtableNo, eit->section_number, eit->version_number);
2120                                 return;
2121                         }
2122                 }
2123         }
2124
2125 //      eDebug("[eEPGCache] New FS sub/sec/ls/lss/ver (%x/%d/%d/%d/%d)", subtableNo, eit->section_number, eit->last_section_number, eit->segment_last_section_number, eit->version_number);
2126         fsstatus->seen(eit->section_number, eit->segment_last_section_number);
2127         if (fsstatus->isCompleted())
2128         {
2129                 m_FreesatTablesToComplete--;
2130         }
2131         cache->sectionRead(data, FREESAT_SCHEDULE_OTHER, this);
2132 }
2133 #endif
2134
2135 RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, const eventData *&result, int direction)
2136 // if t == -1 we search the current event...
2137 {
2138         uniqueEPGKey key(handleGroup(service));
2139
2140         // check if EPG for this service is ready...
2141         eventCache::iterator It = eventDB.find( key );
2142         if ( It != eventDB.end() && !It->second.byEvent.empty() ) // entrys cached ?
2143         {
2144                 if (t==-1)
2145                         t = ::time(0);
2146                 timeMap::iterator i = direction <= 0 ? It->second.byTime.lower_bound(t) :  // find > or equal
2147                         It->second.byTime.upper_bound(t); // just >
2148                 if ( i != It->second.byTime.end() )
2149                 {
2150                         if ( direction < 0 || (direction == 0 && i->first > t) )
2151                         {
2152                                 timeMap::iterator x = i;
2153                                 --x;
2154                                 if ( x != It->second.byTime.end() )
2155                                 {
2156                                         time_t start_time = x->first;
2157                                         if (direction >= 0)
2158                                         {
2159                                                 if (t < start_time)
2160                                                         return -1;
2161                                                 if (t > (start_time+x->second->getDuration()))
2162                                                         return -1;
2163                                         }
2164                                         i = x;
2165                                 }
2166                                 else
2167                                         return -1;
2168                         }
2169                         result = i->second;
2170                         return 0;
2171                 }
2172         }
2173         return -1;
2174 }
2175
2176 RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, Event *& result, int direction)
2177 {
2178         singleLock s(cache_lock);
2179         const eventData *data=0;
2180         RESULT ret = lookupEventTime(service, t, data, direction);
2181         if ( !ret && data )
2182                 result = new Event((uint8_t*)data->get());
2183         return ret;
2184 }
2185
2186 RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, ePtr<eServiceEvent> &result, int direction)
2187 {
2188         singleLock s(cache_lock);
2189         const eventData *data=0;
2190         RESULT ret = lookupEventTime(service, t, data, direction);
2191         result = NULL;
2192         if ( !ret && data )
2193         {
2194                 Event ev((uint8_t*)data->get());
2195                 result = new eServiceEvent();
2196                 const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)service;
2197                 ret = result->parseFrom(&ev, (ref.getTransportStreamID().get()<<16)|ref.getOriginalNetworkID().get());
2198         }
2199         return ret;
2200 }
2201
2202 RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, const eventData *&result )
2203 {
2204         uniqueEPGKey key(handleGroup(service));
2205
2206         eventCache::iterator It = eventDB.find(key);
2207         if (It != eventDB.end())
2208         {
2209                 eventMap::iterator i = It->second.byEvent.find(event_id);
2210                 if ( i != It->second.byEvent.end() )
2211                 {
2212                         result = i->second;
2213                         return 0;
2214                 }
2215                 else
2216                 {
2217                         result = 0;
2218                         eDebug("[eEPGCache] event %04x not found in epgcache", event_id);
2219                 }
2220         }
2221         return -1;
2222 }
2223
2224 RESULT eEPGCache::saveEventToFile(const char* filename, const eServiceReference &service, int eit_event_id, time_t begTime, time_t endTime)
2225 {
2226         RESULT ret = -1;
2227         singleLock s(cache_lock);
2228         const eventData *data = NULL;
2229         if ( eit_event_id != -1 )
2230         {
2231                 eDebug("[eEPGCache] %s epg event id %x", __func__, eit_event_id);
2232                 ret = lookupEventId(service, eit_event_id, data);
2233         }
2234         if ( (ret != 0) && (begTime != -1) )
2235         {
2236                 time_t queryTime = begTime;
2237                 if (endTime != -1)
2238                         queryTime += (endTime - begTime) / 2;
2239                 ret = lookupEventTime(service, queryTime, data);
2240         }
2241         if (ret == 0)
2242         {
2243                 int fd = open(filename, O_CREAT|O_WRONLY, 0666);
2244                 if (fd < 0)
2245                 {
2246                         eDebug("[eEPGCache] Failed to create file: %s", filename);
2247                         return fd;
2248                 }
2249                 const eit_event_struct *event = data->get();
2250                 int evLen = HILO(event->descriptors_loop_length) + 12/*EIT_LOOP_SIZE*/;
2251                 int wr = ::write( fd, event, evLen );
2252                 ::close(fd);
2253                 if ( wr != evLen )
2254                 {
2255                         ::unlink(filename); /* Remove faulty file */
2256                         eDebug("[eEPGCache] eit write error on %s: %m", filename);
2257                         ret = (wr < 0) ? wr : -1;
2258                 }
2259         }
2260         return ret;
2261 }
2262
2263
2264 RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, Event *& result)
2265 {
2266         singleLock s(cache_lock);
2267         const eventData *data=0;
2268         RESULT ret = lookupEventId(service, event_id, data);
2269         if ( !ret && data )
2270                 result = new Event((uint8_t*)data->get());
2271         return ret;
2272 }
2273
2274 RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, ePtr<eServiceEvent> &result)
2275 {
2276         singleLock s(cache_lock);
2277         const eventData *data=0;
2278         RESULT ret = lookupEventId(service, event_id, data);
2279         result = NULL;
2280         if ( !ret && data )
2281         {
2282                 Event ev((uint8_t*)data->get());
2283                 result = new eServiceEvent();
2284                 const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)service;
2285                 ret = result->parseFrom(&ev, (ref.getTransportStreamID().get()<<16)|ref.getOriginalNetworkID().get());
2286         }
2287         return ret;
2288 }
2289
2290 RESULT eEPGCache::startTimeQuery(const eServiceReference &service, time_t begin, int minutes)
2291 {
2292         singleLock s(cache_lock);
2293         const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)handleGroup(service);
2294         if (begin == -1)
2295                 begin = ::time(0);
2296         eventCache::iterator It = eventDB.find(ref);
2297         if ( It != eventDB.end() && !It->second.byTime.empty() )
2298         {
2299                 m_timemap_cursor = It->second.byTime.lower_bound(begin);
2300                 if ( m_timemap_cursor != It->second.byTime.end() )
2301                 {
2302                         if ( m_timemap_cursor->first != begin )
2303                         {
2304                                 timeMap::iterator x = m_timemap_cursor;
2305                                 --x;
2306                                 if ( x != It->second.byTime.end() )
2307                                 {
2308                                         time_t start_time = x->first;
2309                                         if ( begin > start_time && begin < (start_time+x->second->getDuration()))
2310                                                 m_timemap_cursor = x;
2311                                 }
2312                         }
2313                 }
2314
2315                 if (minutes != -1)
2316                         m_timemap_end = It->second.byTime.lower_bound(begin+minutes*60);
2317                 else
2318                         m_timemap_end = It->second.byTime.end();
2319
2320                 currentQueryTsidOnid = (ref.getTransportStreamID().get()<<16) | ref.getOriginalNetworkID().get();
2321                 return m_timemap_cursor == m_timemap_end ? -1 : 0;
2322         }
2323         return -1;
2324 }
2325
2326 RESULT eEPGCache::getNextTimeEntry(Event *&result)
2327 {
2328         if ( m_timemap_cursor != m_timemap_end )
2329         {
2330                 result = new Event((uint8_t*)m_timemap_cursor++->second->get());
2331                 return 0;
2332         }
2333         return -1;
2334 }
2335
2336 RESULT eEPGCache::getNextTimeEntry(ePtr<eServiceEvent> &result)
2337 {
2338         if ( m_timemap_cursor != m_timemap_end )
2339         {
2340                 Event ev((uint8_t*)m_timemap_cursor++->second->get());
2341                 result = new eServiceEvent();
2342                 return result->parseFrom(&ev, currentQueryTsidOnid);
2343         }
2344         return -1;
2345 }
2346
2347 void fillTuple(ePyObject tuple, const char *argstring, int argcount, ePyObject service_reference, eServiceEvent *ptr, ePyObject service_name, ePyObject nowTime, eventData *evData )
2348 {
2349         // eDebug("[eEPGCache] fillTuple arg=%s argcnt=%d, ptr=%d evData=%d", argstring, argcount, ptr ? 1 : 0, evData ? 1 : 0);
2350         ePyObject tmp;
2351         int spos=0, tpos=0;
2352         char c;
2353         while(spos < argcount)
2354         {
2355                 bool inc_refcount=false;
2356                 switch((c=argstring[spos++]))
2357                 {
2358                         case '0': // PyLong 0
2359                                 tmp = PyLong_FromLong(0);
2360                                 break;
2361                         case 'I': // Event Id
2362                                 tmp = evData ? PyLong_FromLong(evData->getEventID()) : (ptr ? PyLong_FromLong(ptr->getEventId()) : ePyObject());
2363                                 break;
2364                         case 'B': // Event Begin Time
2365                                 tmp = ptr ? PyLong_FromLong(ptr->getBeginTime()) : (evData ? PyLong_FromLong(evData->getStartTime()) : ePyObject());
2366                                 break;
2367                         case 'D': // Event Duration
2368                                 tmp = ptr ? PyLong_FromLong(ptr->getDuration()) : (evData ? PyLong_FromLong(evData->getDuration()) : ePyObject());
2369                                 break;
2370                         case 'T': // Event Title
2371                                 tmp = ptr ? PyString_FromString(ptr->getEventName().c_str()) : ePyObject();
2372                                 break;
2373                         case 'S': // Event Short Description
2374                                 tmp = ptr ? PyString_FromString(ptr->getShortDescription().c_str()) : ePyObject();
2375                                 break;
2376                         case 'E': // Event Extended Description
2377                                 tmp = ptr ? PyString_FromString(ptr->getExtendedDescription().c_str()) : ePyObject();
2378                                 break;
2379                         case 'P': // Event Parental Rating
2380                                 tmp = ptr ? ePyObject(ptr->getParentalData()) : ePyObject();
2381                                 break;
2382                         case 'W': // Event Content Description
2383                                 tmp = ptr ? ePyObject(ptr->getGenreData()) : ePyObject();
2384                                 break;
2385                         case 'C': // Current Time
2386                                 tmp = nowTime;
2387                                 inc_refcount = true;
2388                                 break;
2389                         case 'R': // service reference string
2390                                 tmp = service_reference;
2391                                 inc_refcount = true;
2392                                 break;
2393                         case 'n': // short service name
2394                         case 'N': // service name
2395                                 tmp = service_name;
2396                                 inc_refcount = true;
2397                                 break;
2398                         case 'X':
2399                                 ++argcount;
2400                                 continue;
2401                         default:  // ignore unknown
2402                                 tmp = ePyObject();
2403                                 eDebug("[eEPGCache] fillTuple unknown '%c'... insert 'None' in result", c);
2404                 }
2405                 if (!tmp)
2406                 {
2407                         tmp = Py_None;
2408                         inc_refcount = true;
2409                 }
2410                 if (inc_refcount)
2411                         Py_INCREF(tmp);
2412                 PyTuple_SET_ITEM(tuple, tpos++, tmp);
2413         }
2414 }
2415
2416 int handleEvent(eServiceEvent *ptr, ePyObject dest_list, const char* argstring, int argcount, ePyObject service, ePyObject nowTime, ePyObject service_name, ePyObject convertFunc, ePyObject convertFuncArgs)
2417 {
2418         if (convertFunc)
2419         {
2420                 fillTuple(convertFuncArgs, argstring, argcount, service, ptr, service_name, nowTime, 0);
2421                 ePyObject result = PyObject_CallObject(convertFunc, convertFuncArgs);
2422                 if (!result)
2423                 {
2424                         if (service_name)
2425                                 Py_DECREF(service_name);
2426                         if (nowTime)
2427                                 Py_DECREF(nowTime);
2428                         Py_DECREF(convertFuncArgs);
2429                         Py_DECREF(dest_list);
2430                         PyErr_SetString(PyExc_StandardError,
2431                                 "error in convertFunc execute");
2432                         eDebug("[eEPGCache] handleEvent: error in convertFunc execute");
2433                         return -1;
2434                 }
2435                 PyList_Append(dest_list, result);
2436                 Py_DECREF(result);
2437         }
2438         else
2439         {
2440                 ePyObject tuple = PyTuple_New(argcount);
2441                 fillTuple(tuple, argstring, argcount, service, ptr, service_name, nowTime, 0);
2442                 PyList_Append(dest_list, tuple);
2443                 Py_DECREF(tuple);
2444         }
2445         return 0;
2446 }
2447
2448 // here we get a python list
2449 // the first entry in the list is a python string to specify the format of the returned tuples (in a list)
2450 //   0 = PyLong(0)
2451 //   I = Event Id
2452 //   B = Event Begin Time
2453 //   D = Event Duration
2454 //   T = Event Title
2455 //   S = Event Short Description
2456 //   E = Event Extended Description
2457 //   P = Event Parental Rating
2458 //   W = Event Content Description ('W'hat)
2459 //   C = Current Time
2460 //   R = Service Reference
2461 //   N = Service Name
2462 //   n = Short Service Name
2463 //   X = Return a minimum of one tuple per service in the result list... even when no event was found.
2464 //       The returned tuple is filled with all available infos... non avail is filled as None
2465 //       The position and existence of 'X' in the format string has no influence on the result tuple... its completely ignored..
2466 // then for each service follows a tuple
2467 //   first tuple entry is the servicereference (as string... use the ref.toString() function)
2468 //   the second is the type of query
2469 //     2 = event_id
2470 //    -1 = event before given start_time
2471 //     0 = event intersects given start_time
2472 //    +1 = event after given start_time
2473 //   the third
2474 //      when type is eventid it is the event_id
2475 //      when type is time then it is the start_time ( -1 for now_time )
2476 //   the fourth is the end_time .. ( optional .. for query all events in time range)
2477
2478 PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc)
2479 {
2480         ePyObject convertFuncArgs;
2481         int argcount=0;
2482         const char *argstring=NULL;
2483         if (!PyList_Check(list))
2484         {
2485                 PyErr_SetString(PyExc_StandardError,
2486                         "type error");
2487                 eDebug("[eEPGCache] no list");
2488                 return NULL;
2489         }
2490         int listIt=0;
2491         int listSize=PyList_Size(list);
2492         if (!listSize)
2493         {
2494                 PyErr_SetString(PyExc_StandardError,
2495                         "no params given");
2496                 eDebug("[eEPGCache] no params given");
2497                 return NULL;
2498         }
2499         else
2500         {
2501                 ePyObject argv=PyList_GET_ITEM(list, 0); // borrowed reference!
2502                 if (PyString_Check(argv))
2503                 {
2504                         argstring = PyString_AS_STRING(argv);
2505                         ++listIt;
2506                 }
2507                 else
2508                         argstring = "I"; // just event id as default
2509                 argcount = strlen(argstring);
2510 //              eDebug("[eEPGCache] have %d args('%s')", argcount, argstring);
2511         }
2512
2513         bool forceReturnOne = strchr(argstring, 'X') ? true : false;
2514         if (forceReturnOne)
2515                 --argcount;
2516
2517         if (convertFunc)
2518         {
2519                 if (!PyCallable_Check(convertFunc))
2520                 {
2521                         PyErr_SetString(PyExc_StandardError,
2522                                 "convertFunc must be callable");
2523                         eDebug("[eEPGCache] convertFunc is not callable");
2524                         return NULL;
2525                 }
2526                 convertFuncArgs = PyTuple_New(argcount);
2527         }
2528
2529         ePyObject nowTime = strchr(argstring, 'C') ?
2530                 PyLong_FromLong(::time(0)) :
2531                 ePyObject();
2532
2533         int must_get_service_name = strchr(argstring, 'N') ? 1 : strchr(argstring, 'n') ? 2 : 0;
2534
2535         // create dest list
2536         ePyObject dest_list=PyList_New(0);
2537         while(listSize > listIt)
2538         {
2539                 ePyObject item=PyList_GET_ITEM(list, listIt++); // borrowed reference!
2540                 if (PyTuple_Check(item))
2541                 {
2542                         bool service_changed=false;
2543                         int type=0;
2544                         long event_id=-1;
2545                         time_t stime=-1;
2546                         int minutes=0;
2547                         int tupleSize=PyTuple_Size(item);
2548                         int tupleIt=0;
2549                         ePyObject service;
2550                         while(tupleSize > tupleIt)  // parse query args
2551                         {
2552                                 ePyObject entry=PyTuple_GET_ITEM(item, tupleIt); // borrowed reference!
2553                                 switch(tupleIt++)
2554                                 {
2555                                         case 0:
2556                                         {
2557                                                 if (!PyString_Check(entry))
2558                                                 {
2559                                                         eDebug("[eEPGCache] tuple entry 0 is no a string");
2560                                                         goto skip_entry;
2561                                                 }
2562                                                 service = entry;
2563                                                 break;
2564                                         }
2565                                         case 1:
2566                                                 type=PyInt_AsLong(entry);
2567                                                 if (type < -1 || type > 2)
2568                                                 {
2569                                                         eDebug("[eEPGCache] unknown type %d", type);
2570                                                         goto skip_entry;
2571                                                 }
2572                                                 break;
2573                                         case 2:
2574                                                 event_id=stime=PyInt_AsLong(entry);
2575                                                 break;
2576                                         case 3:
2577                                                 minutes=PyInt_AsLong(entry);
2578                                                 break;
2579                                         default:
2580                                                 eDebug("[eEPGCache] unneeded extra argument");
2581                                                 break;
2582                                 }
2583                         }
2584
2585                         if (minutes && stime == -1)
2586                                 stime = ::time(0);
2587
2588                         eServiceReference ref(handleGroup(eServiceReference(PyString_AS_STRING(service))));
2589                         if (ref.type != eServiceReference::idDVB && ref.type != eServiceReference::idServiceMP3)
2590                         {
2591                                 eDebug("[eEPGCache] service reference for epg query is not valid");
2592                                 continue;
2593                         }
2594
2595                         // redirect subservice querys to parent service
2596                         eServiceReferenceDVB &dvb_ref = (eServiceReferenceDVB&)ref;
2597                         if (dvb_ref.getParentTransportStreamID().get()) // linkage subservice
2598                         {
2599                                 eServiceCenterPtr service_center;
2600                                 if (!eServiceCenter::getPrivInstance(service_center))
2601                                 {
2602                                         dvb_ref.setTransportStreamID( dvb_ref.getParentTransportStreamID() );
2603                                         dvb_ref.setServiceID( dvb_ref.getParentServiceID() );
2604                                         dvb_ref.setParentTransportStreamID(eTransportStreamID(0));
2605                                         dvb_ref.setParentServiceID(eServiceID(0));
2606                                         dvb_ref.name="";
2607                                         service = PyString_FromString(dvb_ref.toString().c_str());
2608                                         service_changed = true;
2609                                 }
2610                         }
2611
2612                         ePyObject service_name;
2613                         if (must_get_service_name)
2614                         {
2615                                 ePtr<iStaticServiceInformation> sptr;
2616                                 eServiceCenterPtr service_center;
2617                                 eServiceCenter::getPrivInstance(service_center);
2618                                 if (service_center)
2619                                 {
2620                                         service_center->info(ref, sptr);
2621                                         if (sptr)
2622                                         {
2623                                                 std::string name;
2624                                                 sptr->getName(ref, name);
2625
2626                                                 if (must_get_service_name == 1)
2627                                                 {
2628                                                         size_t pos;
2629                                                         // filter short name brakets
2630                                                         while((pos = name.find("\xc2\x86")) != std::string::npos)
2631                                                                 name.erase(pos,2);
2632                                                         while((pos = name.find("\xc2\x87")) != std::string::npos)
2633                                                                 name.erase(pos,2);
2634                                                 }
2635                                                 else
2636                                                         name = buildShortName(name);
2637
2638                                                 if (name.length())
2639                                                         service_name = PyString_FromString(name.c_str());
2640                                         }
2641                                 }
2642                                 if (!service_name)
2643                                         service_name = PyString_FromString("<n/a>");
2644                         }
2645                         if (minutes)
2646                         {
2647                                 singleLock s(cache_lock);
2648                                 if (!startTimeQuery(ref, stime, minutes))
2649                                 {
2650                                         while ( m_timemap_cursor != m_timemap_end )
2651                                         {
2652                                                 Event ev((uint8_t*)m_timemap_cursor++->second->get());
2653                                                 eServiceEvent evt;
2654                                                 evt.parseFrom(&ev, currentQueryTsidOnid);
2655                                                 if (handleEvent(&evt, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs))
2656                                                         return 0;  // error
2657                                         }
2658                                 }
2659                                 else if (forceReturnOne && handleEvent(0, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs))
2660                                         return 0;  // error
2661                         }
2662                         else
2663                         {
2664                                 eServiceEvent evt;
2665                                 const eventData *ev_data=0;
2666                                 if (stime)
2667                                 {
2668                                         singleLock s(cache_lock);
2669                                         if (type == 2)
2670                                                 lookupEventId(ref, event_id, ev_data);
2671                                         else
2672                                                 lookupEventTime(ref, stime, ev_data, type);
2673                                         if (ev_data)
2674                                         {
2675                                                 const eServiceReferenceDVB &dref = (const eServiceReferenceDVB&)ref;
2676                                                 Event ev((uint8_t*)ev_data->get());
2677                                                 evt.parseFrom(&ev, (dref.getTransportStreamID().get()<<16)|dref.getOriginalNetworkID().get());
2678                                         }
2679                                 }
2680                                 if (ev_data)
2681                                 {
2682                                         if (handleEvent(&evt, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs))
2683                                                 return 0; // error
2684                                 }
2685                                 else if (forceReturnOne && handleEvent(0, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs))
2686                                         return 0; // error
2687                         }
2688                         if (service_changed)
2689                                 Py_DECREF(service);
2690                         if (service_name)
2691                                 Py_DECREF(service_name);
2692                 }
2693 skip_entry:
2694                 ;
2695         }
2696         if (convertFuncArgs)
2697                 Py_DECREF(convertFuncArgs);
2698         if (nowTime)
2699                 Py_DECREF(nowTime);
2700         return dest_list;
2701 }
2702
2703 static void fill_eit_start(eit_event_struct *evt, time_t t)
2704 {
2705     tm *time = gmtime(&t);
2706
2707     int l = 0;
2708     int month = time->tm_mon + 1;
2709     if (month == 1 || month == 2)
2710         l = 1;
2711     int mjd = 14956 + time->tm_mday + (int)((time->tm_year - l) * 365.25) + (int)((month + 1 + l*12) * 30.6001);
2712     evt->start_time_1 = mjd >> 8;
2713     evt->start_time_2 = mjd & 0xFF;
2714
2715     evt->start_time_3 = toBCD(time->tm_hour);
2716     evt->start_time_4 = toBCD(time->tm_min);
2717     evt->start_time_5 = toBCD(time->tm_sec);
2718
2719 }
2720
2721 static void fill_eit_duration(eit_event_struct *evt, int time)
2722 {
2723     //time is given in second
2724     //convert to hour, minutes, seconds
2725     evt->duration_1 = toBCD(time / 3600);
2726     evt->duration_2 = toBCD((time % 3600) / 60);
2727     evt->duration_3 = toBCD((time % 3600) % 60);
2728 }
2729
2730 static inline uint8_t HI(int x) { return (uint8_t) ((x >> 8) & 0xFF); }
2731 static inline uint8_t LO(int x) { return (uint8_t) (x & 0xFF); }
2732 #define SET_HILO(x, val) {x##_hi = ((val) >> 8); x##_lo = (val) & 0xff; }
2733 // convert from set of strings to DVB format (EIT)
2734 void eEPGCache::submitEventData(const std::vector<eServiceReferenceDVB>& serviceRefs, long start,
2735         long duration, const char* title, const char* short_summary,
2736         const char* long_description, char event_type)
2737 {
2738         if (!title)
2739                 return;
2740         static const int EIT_LENGTH = 4108;
2741         static const uint8_t codePage = 0x15; // UTF-8 encoding
2742         uint8_t data[EIT_LENGTH];
2743
2744         eit_t *packet = (eit_t *) data;
2745         packet->table_id = 0x50;
2746         packet->section_syntax_indicator = 1;
2747
2748         packet->version_number = 0;     // eEPGCache::sectionRead() will dig this for the moment
2749         packet->current_next_indicator = 0;
2750         packet->section_number = 0;     // eEPGCache::sectionRead() will dig this for the moment
2751         packet->last_section_number = 0;        // eEPGCache::sectionRead() will dig this for the moment
2752
2753         packet->segment_last_section_number = 0; // eEPGCache::sectionRead() will dig this for the moment
2754         packet->segment_last_table_id = 0x50;
2755
2756         eit_event_t *evt_struct = (eit_event_t*) (data + EIT_SIZE);
2757
2758         uint16_t eventId = start & 0xFFFF;
2759         SET_HILO(evt_struct->event_id, eventId);
2760
2761         //6 bytes start time, 3 bytes duration
2762         fill_eit_start(evt_struct, start);
2763         fill_eit_duration(evt_struct, duration);
2764
2765         evt_struct->running_status = 0;
2766         evt_struct->free_CA_mode = 0;
2767
2768         //no support for different code pages, only DVB's latin1 character set
2769         //TODO: convert text to correct character set (data is probably passed in as UTF-8)
2770         uint8_t *x = (uint8_t *) evt_struct;
2771         x += EIT_LOOP_SIZE;
2772         int nameLength = strnlen(title, 246);
2773         int descLength = short_summary ? strnlen(short_summary, 246 - nameLength) : 0;
2774
2775         eit_short_event_descriptor_struct *short_evt = (eit_short_event_descriptor_struct*) x;
2776         short_evt->descriptor_tag = SHORT_EVENT_DESCRIPTOR;
2777         short_evt->descriptor_length = EIT_SHORT_EVENT_DESCRIPTOR_SIZE + nameLength + descLength + 1 - 2; //+1 for length of short description, -2 for tag and length
2778         if (nameLength) ++short_evt->descriptor_length; // +1 for codepage byte
2779         if (descLength) ++short_evt->descriptor_length;
2780         short_evt->language_code_1 = 'e';
2781         short_evt->language_code_2 = 'n';
2782         short_evt->language_code_3 = 'g';
2783         short_evt->event_name_length = nameLength ? nameLength + 1 : 0;
2784         x = (uint8_t *) short_evt;
2785         x += EIT_SHORT_EVENT_DESCRIPTOR_SIZE;
2786         *x = codePage;
2787         ++x;
2788         memcpy(x, title, nameLength);
2789         x += nameLength;
2790         if (descLength)
2791         {
2792                 *x = descLength + 1;
2793                 ++x;
2794                 *x = codePage;
2795                 ++x;
2796                 memcpy(x, short_summary, descLength);
2797                 x += descLength;
2798         }
2799         else
2800         {
2801                 *x = 0;
2802                 ++x;
2803         }
2804
2805         //Content type
2806         if (event_type != 0)
2807         {
2808                 x[0] = 0x54;
2809                 x[1] = 2;
2810                 x[2] = event_type;
2811                 x[3] = 0;
2812                 x += 4;
2813         }
2814
2815         //Long description
2816         int currentLoopLength = x - (uint8_t*)short_evt;
2817         static const int overheadPerDescriptor = 9; //increase if codepages are added!!!
2818         static const int MAX_LEN = 256 - overheadPerDescriptor;
2819
2820         int textLength = long_description ? strnlen(long_description, EIT_LENGTH) : 0;//EIT_LENGTH is a bit too much, but it's only here as a reasonable end point
2821         int lastDescriptorNumber = (textLength + MAX_LEN-1) / MAX_LEN - 1;
2822         int remainingTextLength = textLength - lastDescriptorNumber * MAX_LEN;
2823
2824         //if long description is too long, just try to fill as many descriptors as possible
2825         while ( (lastDescriptorNumber+1) * 256 + currentLoopLength > EIT_LENGTH - EIT_LOOP_SIZE)
2826         {
2827                 lastDescriptorNumber--;
2828                 remainingTextLength = MAX_LEN;
2829         }
2830
2831         for (int descrIndex = 0; descrIndex <= lastDescriptorNumber; ++descrIndex)
2832         {
2833                 eit_extended_descriptor_struct *ext_evt = (eit_extended_descriptor_struct*) x;
2834                 ext_evt->descriptor_tag = EIT_EXTENDED_EVENT_DESCRIPOR;
2835                 //descriptor header length is 6, including the 2 tag and length bytes
2836                 //so the length field must be: stringlength + 1 (2 4-bits numbers) + 3 (lang code) + 2 bytes for item info length field and text length field
2837                 int currentTextLength = descrIndex < lastDescriptorNumber ? MAX_LEN : remainingTextLength;
2838                 ext_evt->descriptor_length = 6 + currentTextLength + 1;
2839
2840                 ext_evt->descriptor_number = descrIndex;
2841                 ext_evt->last_descriptor_number = lastDescriptorNumber;
2842                 ext_evt->iso_639_2_language_code_1 = 'e';
2843                 ext_evt->iso_639_2_language_code_2 = 'n';
2844                 ext_evt->iso_639_2_language_code_3 = 'g';
2845
2846                 x[6] = 0; //item information (car, year, director, etc. Unsupported for now)
2847                 x[7] = currentTextLength + 1; //length of description string (part in this message)
2848                 x[8] = codePage;
2849                 memcpy(x + 9, &long_description[descrIndex*MAX_LEN], currentTextLength);
2850
2851                 x += 2 + ext_evt->descriptor_length;
2852         }
2853
2854         //TODO: add age and more
2855         int desc_loop_length = x - ((uint8_t*)evt_struct + EIT_LOOP_SIZE);
2856         SET_HILO(evt_struct->descriptors_loop_length, desc_loop_length);
2857
2858         int packet_length = (x - data) - 3; //should add 1 for crc....
2859         SET_HILO(packet->section_length, packet_length);
2860         // Add channelrefs and submit data.
2861         for (std::vector<eServiceReferenceDVB>::const_iterator serviceRef = serviceRefs.begin();
2862                 serviceRef != serviceRefs.end();
2863                 ++serviceRef)
2864         {
2865                 eDVBChannelID chid;
2866                 serviceRef->getChannelID(chid);
2867                 SET_HILO(packet->service_id, serviceRef->getServiceID().get());
2868                 SET_HILO(packet->transport_stream_id, chid.transport_stream_id.get());
2869                 SET_HILO(packet->original_network_id, chid.original_network_id.get());
2870                 sectionRead(data, EPG_IMPORT, 0);
2871         }
2872 }
2873 #undef SET_HILO
2874
2875
2876 void eEPGCache::setEpgHistorySeconds(time_t seconds)
2877 {
2878         historySeconds = seconds;
2879 }
2880
2881 void eEPGCache::setEpgSources(unsigned int mask)
2882 {
2883         enabledSources = mask;
2884 }
2885
2886 unsigned int eEPGCache::getEpgSources()
2887 {
2888         return enabledSources;
2889 }
2890
2891 static const char* getStringFromPython(ePyObject obj)
2892 {
2893         char *result = 0;
2894         if (PyString_Check(obj))
2895         {
2896                 result = PyString_AS_STRING(obj);
2897         }
2898         return result;
2899 }
2900
2901 void eEPGCache::importEvent(ePyObject serviceReference, ePyObject list)
2902 {
2903         importEvents(serviceReference, list);
2904 }
2905
2906 //here we get a python tuple of tuples ;)
2907 // consider it an array of objects with the following data
2908 // 1. start time (long)
2909 // 2. duration (int)
2910 // 3. event title (string)
2911 // 4. short description (string)
2912 // 5. extended description (string)
2913 // 6. event type (byte)
2914 void eEPGCache::importEvents(ePyObject serviceReferences, ePyObject list)
2915 {
2916         std::vector<eServiceReferenceDVB> refs;
2917
2918         if (PyString_Check(serviceReferences))
2919         {
2920                 char *refstr;
2921                 refstr = PyString_AS_STRING(serviceReferences);
2922                 if (!refstr)
2923                 {
2924                         eDebug("[eEPGCache:import] serviceReference string is 0, aborting");
2925                         return;
2926                 }
2927                 refs.push_back(eServiceReferenceDVB(refstr));
2928         }
2929         else if (PyList_Check(serviceReferences))
2930         {
2931                 int nRefs = PyList_Size(serviceReferences);
2932                 for (int i = 0; i < nRefs; ++i)
2933                 {
2934                         PyObject* item = PyList_GET_ITEM(serviceReferences, i);
2935                         char *refstr;
2936                         refstr = PyString_AS_STRING(item);
2937                         if (!refstr)
2938                         {
2939                                 eDebug("[eEPGCache:import] a serviceref item is not a string");
2940                         }
2941                         else
2942                         {
2943                                 refs.push_back(eServiceReferenceDVB(refstr));
2944                         }
2945                 }
2946         }
2947         else
2948         {
2949                 eDebug("[eEPGCache:import] serviceReference string is neither string nor list, aborting");
2950                 return;
2951         }
2952
2953         bool isTuple = PyTuple_Check(list);
2954         if (!isTuple && !PyList_Check(list))
2955         {
2956
2957                 eDebug("[eEPGCache:import] argument 'list' is neither list nor tuple.");
2958                 return;
2959         }
2960
2961         int numberOfEvents = isTuple ? PyTuple_Size(list) : PyList_Size(list);
2962
2963         for (int i = 0; i < numberOfEvents;  ++i)
2964         {
2965                 ePyObject singleEvent = isTuple ? PyTuple_GET_ITEM(list, i) : PyList_GET_ITEM(list, i);
2966                 if (!PyTuple_Check(singleEvent))
2967                 {
2968                         eDebug("[eEPGCache:import] eventdata tuple does not pass PyTuple_Check, aborting");
2969                         return;
2970                 }
2971                 int tupleSize = PyTuple_Size(singleEvent);
2972                 if (tupleSize < 5)
2973                 {
2974                         eDebug("[eEPGCache:import] eventdata tuple does not contain enough fields, aborting");
2975                         return;
2976                 }
2977
2978                 long start = PyLong_AsLong(PyTuple_GET_ITEM(singleEvent, 0));
2979                 long duration = PyInt_AsLong(PyTuple_GET_ITEM(singleEvent, 1));
2980                 const char *title = getStringFromPython(PyTuple_GET_ITEM(singleEvent, 2));
2981                 const char *short_summary = getStringFromPython(PyTuple_GET_ITEM(singleEvent, 3));
2982                 const char *long_description = getStringFromPython(PyTuple_GET_ITEM(singleEvent, 4));
2983                 char event_type = (char) PyInt_AsLong(PyTuple_GET_ITEM(singleEvent, 5));
2984
2985                 Py_BEGIN_ALLOW_THREADS;
2986                 submitEventData(refs, start, duration, title, short_summary, long_description, event_type);
2987                 Py_END_ALLOW_THREADS;
2988         }
2989 }
2990
2991
2992 // here we get a python tuple
2993 // the first entry in the tuple is a python string to specify the format of the returned tuples (in a list)
2994 //   I = Event Id
2995 //   B = Event Begin Time
2996 //   D = Event Duration
2997 //   T = Event Title
2998 //   S = Event Short Description
2999 //   P = Event Parental Rating
3000 //   W = Event Content Description
3001 //   E = Event Extended Description
3002 //   R = Service Reference
3003 //   N = Service Name
3004 //   n = Short Service Name
3005 //  the second tuple entry is the MAX matches value
3006 //  the third tuple entry is the type of query
3007 //     0 = search for similar broadcastings (SIMILAR_BROADCASTINGS_SEARCH)
3008 //     1 = search events with exactly title name (EXACT_TITLE_SEARCH)
3009 //     2 = search events with text in title name (PARTIAL_TITLE_SEARCH)
3010 //     3 = search events starting with title name (START_TITLE_SEARCH)
3011 //  when type is 0 (SIMILAR_BROADCASTINGS_SEARCH)
3012 //   the fourth is the servicereference string
3013 //   the fifth is the eventid
3014 //  when type > 0 (*_TITLE_SEARCH)
3015 //   the fourth is the search text
3016 //   the fifth is
3017 //     0 = case sensitive (CASE_CHECK)
3018 //     1 = case insensitive (NO_CASECHECK)
3019
3020 PyObject *eEPGCache::search(ePyObject arg)
3021 {
3022         ePyObject ret;
3023         std::deque<uint32_t> descr;
3024         int eventid = -1;
3025         const char *argstring=0;
3026         char *refstr=0;
3027         int argcount=0;
3028         int querytype=-1;
3029         bool needServiceEvent=false;
3030         int maxmatches=0;
3031         int must_get_service_name = 0;
3032         bool must_get_service_reference = false;
3033
3034         if (PyTuple_Check(arg))
3035         {
3036                 int tuplesize=PyTuple_Size(arg);
3037                 if (tuplesize > 0)
3038                 {
3039                         ePyObject obj = PyTuple_GET_ITEM(arg,0);
3040                         if (PyString_Check(obj))
3041                         {
3042 #if PY_VERSION_HEX < 0x02060000
3043                                 argcount = PyString_GET_SIZE(obj);
3044 #else
3045                                 argcount = PyString_Size(obj);
3046 #endif
3047                                 argstring = PyString_AS_STRING(obj);
3048                                 for (int i=0; i < argcount; ++i)
3049                                         switch(argstring[i])
3050                                         {
3051                                         case 'S':
3052                                         case 'E':
3053                                         case 'T':
3054                                         case 'P':
3055                                         case 'W':
3056                                                 needServiceEvent=true;
3057                                                 break;
3058                                         case 'N':
3059                                                 must_get_service_name = 1;
3060                                                 break;
3061                                         case 'n':
3062                                                 must_get_service_name = 2;
3063                                                 break;
3064                                         case 'R':
3065                                                 must_get_service_reference = true;
3066                                                 break;
3067                                         default:
3068                                                 break;
3069                                         }
3070                         }
3071                         else
3072                         {
3073                                 PyErr_SetString(PyExc_StandardError,
3074                                         "type error");
3075                                 eDebug("[eEPGCache] tuple arg 0 is not a string");
3076                                 return NULL;
3077                         }
3078                 }
3079                 if (tuplesize > 1)
3080                         maxmatches = PyLong_AsLong(PyTuple_GET_ITEM(arg, 1));
3081                 if (tuplesize > 2)
3082                 {
3083                         querytype = PyLong_AsLong(PyTuple_GET_ITEM(arg, 2));
3084                         if (tuplesize > 4 && querytype == 0)
3085                         {
3086                                 ePyObject obj = PyTuple_GET_ITEM(arg, 3);
3087                                 if (PyString_Check(obj))
3088                                 {
3089                                         refstr = PyString_AS_STRING(obj);
3090                                         eServiceReferenceDVB ref(refstr);
3091                                         if (ref.valid())
3092                                         {
3093                                                 eventid = PyLong_AsLong(PyTuple_GET_ITEM(arg, 4));
3094                                                 singleLock s(cache_lock);
3095                                                 const eventData *evData = 0;
3096                                                 lookupEventId(ref, eventid, evData);
3097                                                 if (evData)
3098                                                 {
3099                                                         // search short and extended event descriptors
3100                                                         for (uint8_t i = 0; i < evData->n_crc; ++i)
3101                                                         {
3102                                                                 uint32_t crc = evData->crc_list[i];
<