make our event loop compatible to my new twisted reactor core
[enigma2.git] / lib / dvb / epgcache.cpp
1 #include <lib/dvb/epgcache.h>
2 #include <lib/dvb/dvb.h>
3
4 #undef EPG_DEBUG  
5
6 #include <time.h>
7 #include <unistd.h>  // for usleep
8 #include <sys/vfs.h> // for statfs
9 // #include <libmd5sum.h>
10 #include <lib/base/eerror.h>
11 #include <Python.h>
12
13 int eventData::CacheSize=0;
14 descriptorMap eventData::descriptors;
15 __u8 eventData::data[4108];
16 extern const uint32_t crc32_table[256];
17
18 eventData::eventData(const eit_event_struct* e, int size, int type)
19         :ByteSize(size&0xFF), type(type&0xFF)
20 {
21         if (!e)
22                 return;
23
24         __u32 descr[65];
25         __u32 *pdescr=descr;
26
27         __u8 *data = (__u8*)e;
28         int ptr=10;
29         int descriptors_length = (data[ptr++]&0x0F) << 8;
30         descriptors_length |= data[ptr++];
31         while ( descriptors_length > 0 )
32         {
33                 __u8 *descr = data+ptr;
34                 int descr_len = descr[1]+2;
35
36                 __u32 crc = 0;
37                 int cnt=0;
38                 while(cnt++ < descr_len)
39                         crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ data[ptr++]) & 0xFF];
40
41                 descriptorMap::iterator it =
42                         descriptors.find(crc);
43                 if ( it == descriptors.end() )
44                 {
45                         CacheSize+=descr_len;
46                         __u8 *d = new __u8[descr_len];
47                         memcpy(d, descr, descr_len);
48                         descriptors[crc] = descriptorPair(1, d);
49                 }
50                 else
51                         ++it->second.first;
52
53                 *pdescr++=crc;
54                 descriptors_length -= descr_len;
55         }
56         ByteSize = 12+((pdescr-descr)*4);
57         EITdata = new __u8[ByteSize];
58         CacheSize+=ByteSize;
59         memcpy(EITdata, (__u8*) e, 12);
60         memcpy(EITdata+12, descr, ByteSize-12);
61 }
62
63 const eit_event_struct* eventData::get() const
64 {
65         int pos = 12;
66         int tmp = ByteSize-12;
67
68         memcpy(data, EITdata, 12);
69         __u32 *p = (__u32*)(EITdata+12);
70         while(tmp>0)
71         {
72                 descriptorMap::iterator it =
73                         descriptors.find(*p++);
74                 if ( it != descriptors.end() )
75                 {
76                         int b = it->second.second[1]+2;
77                         memcpy(data+pos, it->second.second, b );
78                         pos += b;
79                 }
80                 tmp-=4;
81         }
82
83         return (const eit_event_struct*)data;
84 }
85
86 eventData::~eventData()
87 {
88         if ( ByteSize )
89         {
90                 CacheSize-=ByteSize;
91                 ByteSize-=12;
92                 __u32 *d = (__u32*)(EITdata+12);
93                 while(ByteSize)
94                 {
95                         descriptorMap::iterator it =
96                                 descriptors.find(*d++);
97                         if ( it != descriptors.end() )
98                         {
99                                 descriptorPair &p = it->second;
100                                 if (!--p.first) // no more used descriptor
101                                 {
102                                         CacheSize -= it->second.second[1];
103                                         delete [] it->second.second;    // free descriptor memory
104                                         descriptors.erase(it);  // remove entry from descriptor map
105                                 }
106                         }
107                         ByteSize-=4;
108                 }
109                 delete [] EITdata;
110         }
111 }
112
113 void eventData::load(FILE *f)
114 {
115         int size=0;
116         int id=0;
117         __u8 header[2];
118         descriptorPair p;
119         fread(&size, sizeof(int), 1, f);
120         while(size)
121         {
122                 fread(&id, sizeof(__u32), 1, f);
123                 fread(&p.first, sizeof(int), 1, f);
124                 fread(header, 2, 1, f);
125                 int bytes = header[1]+2;
126                 p.second = new __u8[bytes];
127                 p.second[0] = header[0];
128                 p.second[1] = header[1];
129                 fread(p.second+2, bytes-2, 1, f);
130                 descriptors[id]=p;
131                 --size;
132                 CacheSize+=bytes;
133         }
134 }
135
136 void eventData::save(FILE *f)
137 {
138         int size=descriptors.size();
139         descriptorMap::iterator it(descriptors.begin());
140         fwrite(&size, sizeof(int), 1, f);
141         while(size)
142         {
143                 fwrite(&it->first, sizeof(__u32), 1, f);
144                 fwrite(&it->second.first, sizeof(int), 1, f);
145                 fwrite(it->second.second, it->second.second[1]+2, 1, f);
146                 ++it;
147                 --size;
148         }
149 }
150
151 eEPGCache* eEPGCache::instance;
152 pthread_mutex_t eEPGCache::cache_lock=
153         PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
154 pthread_mutex_t eEPGCache::channel_map_lock=
155         PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
156
157 DEFINE_REF(eEPGCache)
158
159 eEPGCache::eEPGCache()
160         :messages(this,1), cleanTimer(this)//, paused(0)
161 {
162         eDebug("[EPGC] Initialized EPGCache");
163
164         CONNECT(messages.recv_msg, eEPGCache::gotMessage);
165         CONNECT(eDVBLocalTimeHandler::getInstance()->m_timeUpdated, eEPGCache::timeUpdated);
166         CONNECT(cleanTimer.timeout, eEPGCache::cleanLoop);
167
168         ePtr<eDVBResourceManager> res_mgr;
169         eDVBResourceManager::getInstance(res_mgr);
170         if (!res_mgr)
171                 eDebug("[eEPGCache] no resource manager !!!!!!!");
172         else
173                 res_mgr->connectChannelAdded(slot(*this,&eEPGCache::DVBChannelAdded), m_chanAddedConn);
174         instance=this;
175 }
176
177 void eEPGCache::timeUpdated()
178 {
179         if ( !thread_running() )
180         {
181                 eDebug("[EPGC] time updated.. start EPG Mainloop");
182                 run();
183         }
184         else
185                 messages.send(Message(Message::timeChanged));
186 }
187
188 void eEPGCache::DVBChannelAdded(eDVBChannel *chan)
189 {
190         if ( chan )
191         {
192 //              eDebug("[eEPGCache] add channel %p", chan);
193                 channel_data *data = new channel_data(this);
194                 data->channel = chan;
195                 data->prevChannelState = -1;
196                 singleLock s(channel_map_lock);
197                 m_knownChannels.insert( std::pair<iDVBChannel*, channel_data* >(chan, data) );
198                 chan->connectStateChange(slot(*this, &eEPGCache::DVBChannelStateChanged), data->m_stateChangedConn);
199         }
200 }
201
202 void eEPGCache::DVBChannelRunning(iDVBChannel *chan)
203 {
204         singleLock s(channel_map_lock);
205         channelMapIterator it =
206                 m_knownChannels.find(chan);
207         if ( it == m_knownChannels.end() )
208                 eDebug("[eEPGCache] will start non existing channel %p !!!", chan);
209         else
210         {
211                 channel_data &data = *it->second;
212                 ePtr<eDVBResourceManager> res_mgr;
213                 if ( eDVBResourceManager::getInstance( res_mgr ) )
214                         eDebug("[eEPGCache] no res manager!!");
215                 else
216                 {
217                         ePtr<iDVBDemux> demux;
218                         if ( data.channel->getDemux(demux, 0) )
219                         {
220                                 eDebug("[eEPGCache] no demux!!");
221                                 return;
222                         }
223                         else
224                         {
225                                 RESULT res = demux->createSectionReader( this, data.m_NowNextReader );
226                                 if ( res )
227                                 {
228                                         eDebug("[eEPGCache] couldnt initialize nownext reader!!");
229                                         return;
230                                 }
231
232                                 res = demux->createSectionReader( this, data.m_ScheduleReader );
233                                 if ( res )
234                                 {
235                                         eDebug("[eEPGCache] couldnt initialize schedule reader!!");
236                                         return;
237                                 }
238
239                                 res = demux->createSectionReader( this, data.m_ScheduleOtherReader );
240                                 if ( res )
241                                 {
242                                         eDebug("[eEPGCache] couldnt initialize schedule other reader!!");
243                                         return;
244                                 }
245
246                                 messages.send(Message(Message::startChannel, chan));
247                                 // -> gotMessage -> changedService
248                         }
249                 }
250         }
251 }
252
253 void eEPGCache::DVBChannelStateChanged(iDVBChannel *chan)
254 {
255         channelMapIterator it =
256                 m_knownChannels.find(chan);
257         if ( it != m_knownChannels.end() )
258         {
259                 int state=0;
260                 chan->getState(state);
261                 if ( it->second->prevChannelState != state )
262                 {
263                         switch (state)
264                         {
265                                 case iDVBChannel::state_ok:
266                                 {
267                                         eDebug("[eEPGCache] channel %p running", chan);
268                                         DVBChannelRunning(chan);
269                                         break;
270                                 }
271                                 case iDVBChannel::state_release:
272                                 {
273                                         eDebug("[eEPGCache] remove channel %p", chan);
274                                         messages.send(Message(Message::leaveChannel, chan));
275                                         while(!it->second->can_delete)
276                                                 usleep(1000);
277                                         delete it->second;
278                                         m_knownChannels.erase(it);
279                                         // -> gotMessage -> abortEPG
280                                         break;
281                                 }
282                                 default: // ignore all other events
283                                         return;
284                         }
285                         it->second->prevChannelState = state;
286                 }
287         }
288 }
289
290 void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel)
291 {
292         eit_t *eit = (eit_t*) data;
293
294         int len=HILO(eit->section_length)-1;//+3-4;
295         int ptr=EIT_SIZE;
296         if ( ptr >= len )
297                 return;
298
299         // This fixed the EPG on the Multichoice irdeto systems
300         // the EIT packet is non-compliant.. their EIT packet stinks
301         if ( data[ptr-1] < 0x40 )
302                 --ptr;
303
304         uniqueEPGKey service( HILO(eit->service_id), HILO(eit->original_network_id), HILO(eit->transport_stream_id) );
305         eit_event_struct* eit_event = (eit_event_struct*) (data+ptr);
306         int eit_event_size;
307         int duration;
308
309         time_t TM = parseDVBtime( eit_event->start_time_1, eit_event->start_time_2,     eit_event->start_time_3, eit_event->start_time_4, eit_event->start_time_5);
310         time_t now = time(0)+eDVBLocalTimeHandler::getInstance()->difference();
311
312         if ( TM != 3599 && TM > -1)
313                 channel->haveData |= source;
314
315         singleLock s(cache_lock);
316         // hier wird immer eine eventMap zurück gegeben.. entweder eine vorhandene..
317         // oder eine durch [] erzeugte
318         std::pair<eventMap,timeMap> &servicemap = eventDB[service];
319         eventMap::iterator prevEventIt = servicemap.first.end();
320         timeMap::iterator prevTimeIt = servicemap.second.end();
321
322         while (ptr<len)
323         {
324                 eit_event_size = HILO(eit_event->descriptors_loop_length)+EIT_LOOP_SIZE;
325
326                 duration = fromBCD(eit_event->duration_1)*3600+fromBCD(eit_event->duration_2)*60+fromBCD(eit_event->duration_3);
327                 TM = parseDVBtime(
328                         eit_event->start_time_1,
329                         eit_event->start_time_2,
330                         eit_event->start_time_3,
331                         eit_event->start_time_4,
332                         eit_event->start_time_5);
333
334                 if ( TM == 3599 )
335                         goto next;
336
337                 if ( TM != 3599 && (TM+duration < now || TM > now+14*24*60*60) )
338                         goto next;
339
340                 if ( now <= (TM+duration) || TM == 3599 /*NVOD Service*/ )  // old events should not be cached
341                 {
342                         __u16 event_id = HILO(eit_event->event_id);
343 //                      eDebug("event_id is %d sid is %04x", event_id, service.sid);
344
345                         eventData *evt = 0;
346                         int ev_erase_count = 0;
347                         int tm_erase_count = 0;
348
349                         // search in eventmap
350                         eventMap::iterator ev_it =
351                                 servicemap.first.find(event_id);
352
353                         // entry with this event_id is already exist ?
354                         if ( ev_it != servicemap.first.end() )
355                         {
356                                 if ( source > ev_it->second->type )  // update needed ?
357                                         goto next; // when not.. the skip this entry
358
359                                 // search this event in timemap
360                                 timeMap::iterator tm_it_tmp = 
361                                         servicemap.second.find(ev_it->second->getStartTime());
362
363                                 if ( tm_it_tmp != servicemap.second.end() )
364                                 {
365                                         if ( tm_it_tmp->first == TM ) // correct eventData
366                                         {
367                                                 // exempt memory
368                                                 delete ev_it->second;
369                                                 evt = new eventData(eit_event, eit_event_size, source);
370                                                 ev_it->second=evt;
371                                                 tm_it_tmp->second=evt;
372                                                 goto next;
373                                         }
374                                         else
375                                         {
376                                                 tm_erase_count++;
377                                                 // delete the found record from timemap
378                                                 servicemap.second.erase(tm_it_tmp);
379                                                 prevTimeIt=servicemap.second.end();
380                                         }
381                                 }
382                         }
383
384                         // search in timemap, for check of a case if new time has coincided with time of other event 
385                         // or event was is not found in eventmap
386                         timeMap::iterator tm_it =
387                                 servicemap.second.find(TM);
388
389                         if ( tm_it != servicemap.second.end() )
390                         {
391                                 // i think, if event is not found on eventmap, but found on timemap updating nevertheless demands
392 #if 0
393                                 if ( source > tm_it->second->type && tm_erase_count == 0 ) // update needed ?
394                                         goto next; // when not.. the skip this entry
395 #endif
396
397                                 // search this time in eventmap
398                                 eventMap::iterator ev_it_tmp = 
399                                         servicemap.first.find(tm_it->second->getEventID());
400
401                                 if ( ev_it_tmp != servicemap.first.end() )
402                                 {
403                                         ev_erase_count++;                               
404                                         // delete the found record from eventmap
405                                         servicemap.first.erase(ev_it_tmp);
406                                         prevEventIt=servicemap.first.end();
407                                 }
408                         }
409                         
410                         evt = new eventData(eit_event, eit_event_size, source);
411 #if EPG_DEBUG
412                         bool consistencyCheck=true;
413 #endif
414                         if (ev_erase_count > 0 && tm_erase_count > 0) // 2 different pairs have been removed
415                         {
416                                 // exempt memory
417                                 delete ev_it->second; 
418                                 delete tm_it->second;
419                                 ev_it->second=evt;
420                                 tm_it->second=evt;
421                         }
422                         else if (ev_erase_count == 0 && tm_erase_count > 0) 
423                         {
424                                 // exempt memory
425                                 delete ev_it->second;
426                                 tm_it=prevTimeIt=servicemap.second.insert( prevTimeIt, std::pair<const time_t, eventData*>( TM, evt ) );
427                                 ev_it->second=evt;
428                         }
429                         else if (ev_erase_count > 0 && tm_erase_count == 0)
430                         {
431                                 // exempt memory
432                                 delete tm_it->second;
433                                 ev_it=prevEventIt=servicemap.first.insert( prevEventIt, std::pair<const __u16, eventData*>( event_id, evt) );
434                                 tm_it->second=evt;
435                         }
436                         else // added new eventData
437                         {
438 #if EPG_DEBUG
439                                 consistencyCheck=false;
440 #endif
441                                 prevEventIt=servicemap.first.insert( prevEventIt, std::pair<const __u16, eventData*>( event_id, evt) );
442                                 prevTimeIt=servicemap.second.insert( prevTimeIt, std::pair<const time_t, eventData*>( TM, evt ) );
443                         }
444 #if EPG_DEBUG
445                         if ( consistencyCheck )
446                         {
447                                 if ( tm_it->second != evt || ev_it->second != evt )
448                                         eFatal("tm_it->second != ev_it->second");
449                                 else if ( tm_it->second->getStartTime() != tm_it->first )
450                                         eFatal("event start_time(%d) non equal timemap key(%d)", 
451                                                 tm_it->second->getStartTime(), tm_it->first );
452                                 else if ( tm_it->first != TM )
453                                         eFatal("timemap key(%d) non equal TM(%d)", 
454                                                 tm_it->first, TM);
455                                 else if ( ev_it->second->getEventID() != ev_it->first )
456                                         eFatal("event_id (%d) non equal event_map key(%d)",
457                                                 ev_it->second->getEventID(), ev_it->first);
458                                 else if ( ev_it->first != event_id )
459                                         eFatal("eventmap key(%d) non equal event_id(%d)", 
460                                                 ev_it->first, event_id );
461                         }
462 #endif
463                 }
464 next:
465 #if EPG_DEBUG
466                 if ( servicemap.first.size() != servicemap.second.size() )
467                 {
468                         FILE *f = fopen("/hdd/event_map.txt", "w+");
469                         int i=0;
470                         for (eventMap::iterator it(servicemap.first.begin())
471                                 ; it != servicemap.first.end(); ++it )
472                                 fprintf(f, "%d(key %d) -> time %d, event_id %d, data %p\n", 
473                                         i++, (int)it->first, (int)it->second->getStartTime(), (int)it->second->getEventID(), it->second );
474                         fclose(f);
475                         f = fopen("/hdd/time_map.txt", "w+");
476                         i=0;
477                         for (timeMap::iterator it(servicemap.second.begin())
478                                 ; it != servicemap.second.end(); ++it )
479                                         fprintf(f, "%d(key %d) -> time %d, event_id %d, data %p\n", 
480                                                 i++, (int)it->first, (int)it->second->getStartTime(), (int)it->second->getEventID(), it->second );
481                         fclose(f);
482
483                         eFatal("(1)map sizes not equal :( sid %04x tsid %04x onid %04x size %d size2 %d", 
484                                 service.sid, service.tsid, service.onid, 
485                                 servicemap.first.size(), servicemap.second.size() );
486                 }
487 #endif
488                 ptr += eit_event_size;
489                 eit_event=(eit_event_struct*)(((__u8*)eit_event)+eit_event_size);
490         }
491 }
492
493 void eEPGCache::flushEPG(const uniqueEPGKey & s)
494 {
495         eDebug("[EPGC] flushEPG %d", (int)(bool)s);
496         singleLock l(cache_lock);
497         if (s)  // clear only this service
498         {
499                 eventCache::iterator it = eventDB.find(s);
500                 if ( it != eventDB.end() )
501                 {
502                         eventMap &evMap = it->second.first;
503                         timeMap &tmMap = it->second.second;
504                         tmMap.clear();
505                         for (eventMap::iterator i = evMap.begin(); i != evMap.end(); ++i)
506                                 delete i->second;
507                         evMap.clear();
508                         eventDB.erase(it);
509
510                         // TODO .. search corresponding channel for removed service and remove this channel from lastupdated map
511                 }
512         }
513         else // clear complete EPG Cache
514         {
515                 for (eventCache::iterator it(eventDB.begin());
516                         it != eventDB.end(); ++it)
517                 {
518                         eventMap &evMap = it->second.first;
519                         timeMap &tmMap = it->second.second;
520                         for (eventMap::iterator i = evMap.begin(); i != evMap.end(); ++i)
521                                 delete i->second;
522                         evMap.clear();
523                         tmMap.clear();
524                 }
525                 eventDB.clear();
526                 channelLastUpdated.clear();
527                 singleLock m(channel_map_lock);
528                 for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
529                         it->second->startEPG();
530         }
531         eDebug("[EPGC] %i bytes for cache used", eventData::CacheSize);
532 }
533
534 void eEPGCache::cleanLoop()
535 {
536         singleLock s(cache_lock);
537         if (!eventDB.empty())
538         {
539                 eDebug("[EPGC] start cleanloop");
540
541                 time_t now = time(0)+eDVBLocalTimeHandler::getInstance()->difference();
542
543                 for (eventCache::iterator DBIt = eventDB.begin(); DBIt != eventDB.end(); DBIt++)
544                 {
545                         for (timeMap::iterator It = DBIt->second.second.begin(); It != DBIt->second.second.end() && It->first < now;)
546                         {
547                                 if ( now > (It->first+It->second->getDuration()) )  // outdated normal entry (nvod references to)
548                                 {
549                                         // remove entry from eventMap
550                                         eventMap::iterator b(DBIt->second.first.find(It->second->getEventID()));
551                                         if ( b != DBIt->second.first.end() )
552                                         {
553                                                 // release Heap Memory for this entry   (new ....)
554 //                                              eDebug("[EPGC] delete old event (evmap)");
555                                                 DBIt->second.first.erase(b);
556                                         }
557
558                                         // remove entry from timeMap
559 //                                      eDebug("[EPGC] release heap mem");
560                                         delete It->second;
561                                         DBIt->second.second.erase(It++);
562 //                                      eDebug("[EPGC] delete old event (timeMap)");
563                                 }
564                                 else
565                                         ++It;
566                         }
567                 }
568                 eDebug("[EPGC] stop cleanloop");
569                 eDebug("[EPGC] %i bytes for cache used", eventData::CacheSize);
570         }
571         cleanTimer.start(CLEAN_INTERVAL,true);
572 }
573
574 eEPGCache::~eEPGCache()
575 {
576         messages.send(Message::quit);
577         kill(); // waiting for thread shutdown
578         singleLock s(cache_lock);
579         for (eventCache::iterator evIt = eventDB.begin(); evIt != eventDB.end(); evIt++)
580                 for (eventMap::iterator It = evIt->second.first.begin(); It != evIt->second.first.end(); It++)
581                         delete It->second;
582 }
583
584 void eEPGCache::gotMessage( const Message &msg )
585 {
586         switch (msg.type)
587         {
588                 case Message::flush:
589                         flushEPG(msg.service);
590                         break;
591                 case Message::startChannel:
592                 {
593                         singleLock s(channel_map_lock);
594                         channelMapIterator channel =
595                                 m_knownChannels.find(msg.channel);
596                         if ( channel != m_knownChannels.end() )
597                                 channel->second->startChannel();
598                         break;
599                 }
600                 case Message::leaveChannel:
601                 {
602                         singleLock s(channel_map_lock);
603                         channelMapIterator channel =
604                                 m_knownChannels.find(msg.channel);
605                         if ( channel != m_knownChannels.end() )
606                                 channel->second->abortEPG();
607                         break;
608                 }
609                 case Message::quit:
610                         quit(0);
611                         break;
612                 case Message::timeChanged:
613                         cleanLoop();
614                         break;
615                 default:
616                         eDebug("unhandled EPGCache Message!!");
617                         break;
618         }
619 }
620
621 void eEPGCache::thread()
622 {
623         nice(4);
624         load();
625         cleanLoop();
626         run();
627         save();
628 }
629
630 void eEPGCache::load()
631 {
632         FILE *f = fopen("/hdd/epg.dat", "r");
633         if (f)
634         {
635                 unsigned char md5_saved[16];
636                 unsigned char md5[16];
637                 int size=0;
638                 int cnt=0;
639                 bool md5ok=false;
640 #if 0
641                 if (!md5_file("/hdd/epg.dat", 1, md5))
642                 {
643                         FILE *f = fopen("/hdd/epg.dat.md5", "r");
644                         if (f)
645                         {
646                                 fread( md5_saved, 16, 1, f);
647                                 fclose(f);
648                                 if ( !memcmp(md5_saved, md5, 16) )
649                                         md5ok=true;
650                         }
651                 }
652                 if ( md5ok )
653 #endif
654                 {
655                         char text1[13];
656                         fread( text1, 13, 1, f);
657                         if ( !strncmp( text1, "ENIGMA_EPG_V4", 13) )
658                         {
659                                 fread( &size, sizeof(int), 1, f);
660                                 while(size--)
661                                 {
662                                         uniqueEPGKey key;
663                                         eventMap evMap;
664                                         timeMap tmMap;
665                                         int size=0;
666                                         fread( &key, sizeof(uniqueEPGKey), 1, f);
667                                         fread( &size, sizeof(int), 1, f);
668                                         while(size--)
669                                         {
670                                                 __u8 len=0;
671                                                 __u8 type=0;
672                                                 eventData *event=0;
673                                                 fread( &type, sizeof(__u8), 1, f);
674                                                 fread( &len, sizeof(__u8), 1, f);
675                                                 event = new eventData(0, len, type);
676                                                 event->EITdata = new __u8[len];
677                                                 eventData::CacheSize+=len;
678                                                 fread( event->EITdata, len, 1, f);
679                                                 evMap[ event->getEventID() ]=event;
680                                                 tmMap[ event->getStartTime() ]=event;
681                                                 ++cnt;
682                                         }
683                                         eventDB[key]=std::pair<eventMap,timeMap>(evMap,tmMap);
684                                 }
685                                 eventData::load(f);
686                                 eDebug("%d events read from /hdd/epg.dat", cnt);
687                         }
688                         else
689                                 eDebug("[EPGC] don't read old epg database");
690                         fclose(f);
691                 }
692         }
693 }
694
695 void eEPGCache::save()
696 {
697         struct statfs s;
698         off64_t tmp;
699         if (statfs("/hdd", &s)<0)
700                 tmp=0;
701         else
702         {
703                 tmp=s.f_blocks;
704                 tmp*=s.f_bsize;
705         }
706
707         // prevent writes to builtin flash
708         if ( tmp < 1024*1024*50 ) // storage size < 50MB
709                 return;
710
711         // check for enough free space on storage
712         tmp=s.f_bfree;
713         tmp*=s.f_bsize;
714         if ( tmp < (eventData::CacheSize*12)/10 ) // 20% overhead
715                 return;
716
717         FILE *f = fopen("/hdd/epg.dat", "w");
718         int cnt=0;
719         if ( f )
720         {
721                 const char *text = "ENIGMA_EPG_V4";
722                 fwrite( text, 13, 1, f );
723                 int size = eventDB.size();
724                 fwrite( &size, sizeof(int), 1, f );
725                 for (eventCache::iterator service_it(eventDB.begin()); service_it != eventDB.end(); ++service_it)
726                 {
727                         timeMap &timemap = service_it->second.second;
728                         fwrite( &service_it->first, sizeof(uniqueEPGKey), 1, f);
729                         size = timemap.size();
730                         fwrite( &size, sizeof(int), 1, f);
731                         for (timeMap::iterator time_it(timemap.begin()); time_it != timemap.end(); ++time_it)
732                         {
733                                 __u8 len = time_it->second->ByteSize;
734                                 fwrite( &time_it->second->type, sizeof(__u8), 1, f );
735                                 fwrite( &len, sizeof(__u8), 1, f);
736                                 fwrite( time_it->second->EITdata, len, 1, f);
737                                 ++cnt;
738                         }
739                 }
740                 eDebug("%d events written to /hdd/epg.dat", cnt);
741                 eventData::save(f);
742                 fclose(f);
743 #if 0
744                 unsigned char md5[16];
745                 if (!md5_file("/hdd/epg.dat", 1, md5))
746                 {
747                         FILE *f = fopen("/hdd/epg.dat.md5", "w");
748                         if (f)
749                         {
750                                 fwrite( md5, 16, 1, f);
751                                 fclose(f);
752                         }
753                 }
754 #endif
755         }
756 }
757
758 eEPGCache::channel_data::channel_data(eEPGCache *ml)
759         :cache(ml)
760         ,abortTimer(ml), zapTimer(ml)
761         ,state(0), isRunning(0), haveData(0), can_delete(1)
762 {
763         CONNECT(zapTimer.timeout, eEPGCache::channel_data::startEPG);
764         CONNECT(abortTimer.timeout, eEPGCache::channel_data::abortNonAvail);
765 }
766
767 bool eEPGCache::channel_data::finishEPG()
768 {
769         if (!isRunning)  // epg ready
770         {
771                 eDebug("[EPGC] stop caching events(%d)", time(0)+eDVBLocalTimeHandler::getInstance()->difference());
772                 zapTimer.start(UPDATE_INTERVAL, 1);
773                 eDebug("[EPGC] next update in %i min", UPDATE_INTERVAL / 60000);
774                 for (int i=0; i < 3; ++i)
775                 {
776                         seenSections[i].clear();
777                         calcedSections[i].clear();
778                 }
779                 singleLock l(cache->cache_lock);
780                 cache->channelLastUpdated[channel->getChannelID()] = time(0)+eDVBLocalTimeHandler::getInstance()->difference();
781                 can_delete=1;
782                 return true;
783         }
784         return false;
785 }
786
787 void eEPGCache::channel_data::startEPG()
788 {
789         eDebug("[EPGC] start caching events(%d)", eDVBLocalTimeHandler::getInstance()->difference()+time(0));
790         state=0;
791         haveData=0;
792         can_delete=0;
793         for (int i=0; i < 3; ++i)
794         {
795                 seenSections[i].clear();
796                 calcedSections[i].clear();
797         }
798
799         eDVBSectionFilterMask mask;
800         memset(&mask, 0, sizeof(mask));
801         mask.pid = 0x12;
802         mask.flags = eDVBSectionFilterMask::rfCRC;
803
804         mask.data[0] = 0x4E;
805         mask.mask[0] = 0xFE;
806         m_NowNextReader->connectRead(slot(*this, &eEPGCache::channel_data::readData), m_NowNextConn);
807         m_NowNextReader->start(mask);
808         isRunning |= NOWNEXT;
809
810         mask.data[0] = 0x50;
811         mask.mask[0] = 0xF0;
812         m_ScheduleReader->connectRead(slot(*this, &eEPGCache::channel_data::readData), m_ScheduleConn);
813         m_ScheduleReader->start(mask);
814         isRunning |= SCHEDULE;
815
816         mask.data[0] = 0x60;
817         mask.mask[0] = 0xF0;
818         m_ScheduleOtherReader->connectRead(slot(*this, &eEPGCache::channel_data::readData), m_ScheduleOtherConn);
819         m_ScheduleOtherReader->start(mask);
820         isRunning |= SCHEDULE_OTHER;
821
822         abortTimer.start(7000,true);
823 }
824
825 void eEPGCache::channel_data::abortNonAvail()
826 {
827         if (!state)
828         {
829                 if ( !(haveData&eEPGCache::NOWNEXT) && (isRunning&eEPGCache::NOWNEXT) )
830                 {
831                         eDebug("[EPGC] abort non avail nownext reading");
832                         isRunning &= ~eEPGCache::NOWNEXT;
833                         m_NowNextReader->stop();
834                         m_NowNextConn=0;
835                 }
836                 if ( !(haveData&eEPGCache::SCHEDULE) && (isRunning&eEPGCache::SCHEDULE) )
837                 {
838                         eDebug("[EPGC] abort non avail schedule reading");
839                         isRunning &= ~SCHEDULE;
840                         m_ScheduleReader->stop();
841                         m_ScheduleConn=0;
842                 }
843                 if ( !(haveData&eEPGCache::SCHEDULE_OTHER) && (isRunning&eEPGCache::SCHEDULE_OTHER) )
844                 {
845                         eDebug("[EPGC] abort non avail schedule_other reading");
846                         isRunning &= ~SCHEDULE_OTHER;
847                         m_ScheduleOtherReader->stop();
848                         m_ScheduleOtherConn=0;
849                 }
850                 if ( isRunning )
851                         abortTimer.start(90000, true);
852                 else
853                 {
854                         ++state;
855                         for (int i=0; i < 3; ++i)
856                         {
857                                 seenSections[i].clear();
858                                 calcedSections[i].clear();
859                         }
860                         can_delete=1;
861                 }
862         }
863         ++state;
864 }
865
866 void eEPGCache::channel_data::startChannel()
867 {
868         updateMap::iterator It = cache->channelLastUpdated.find( channel->getChannelID() );
869
870         int update = ( It != cache->channelLastUpdated.end() ? ( UPDATE_INTERVAL - ( (time(0)+eDVBLocalTimeHandler::getInstance()->difference()-It->second) * 1000 ) ) : ZAP_DELAY );
871
872         if (update < ZAP_DELAY)
873                 update = ZAP_DELAY;
874
875         zapTimer.start(update, 1);
876         if (update >= 60000)
877                 eDebug("[EPGC] next update in %i min", update/60000);
878         else if (update >= 1000)
879                 eDebug("[EPGC] next update in %i sec", update/1000);
880 }
881
882 void eEPGCache::channel_data::abortEPG()
883 {
884         for (int i=0; i < 3; ++i)
885         {
886                 seenSections[i].clear();
887                 calcedSections[i].clear();
888         }
889         abortTimer.stop();
890         zapTimer.stop();
891         if (isRunning)
892         {
893                 eDebug("[EPGC] abort caching events !!");
894                 if (isRunning & eEPGCache::SCHEDULE)
895                 {
896                         isRunning &= ~eEPGCache::SCHEDULE;
897                         m_ScheduleReader->stop();
898                         m_ScheduleConn=0;
899                 }
900                 if (isRunning & eEPGCache::NOWNEXT)
901                 {
902                         isRunning &= ~eEPGCache::NOWNEXT;
903                         m_NowNextReader->stop();
904                         m_NowNextConn=0;
905                 }
906                 if (isRunning & SCHEDULE_OTHER)
907                 {
908                         isRunning &= ~eEPGCache::SCHEDULE_OTHER;
909                         m_ScheduleOtherReader->stop();
910                         m_ScheduleOtherConn=0;
911                 }
912                 can_delete=1;
913         }
914 }
915
916 void eEPGCache::channel_data::readData( const __u8 *data)
917 {
918         if (!data)
919                 eDebug("get Null pointer from section reader !!");
920         else
921         {
922                 int source;
923                 int map;
924                 iDVBSectionReader *reader=NULL;
925                 switch(data[0])
926                 {
927                         case 0x4E ... 0x4F:
928                                 reader=m_NowNextReader;
929                                 source=eEPGCache::NOWNEXT;
930                                 map=0;
931                                 break;
932                         case 0x50 ... 0x5F:
933                                 reader=m_ScheduleReader;
934                                 source=eEPGCache::SCHEDULE;
935                                 map=1;
936                                 break;
937                         case 0x60 ... 0x6F:
938                                 reader=m_ScheduleOtherReader;
939                                 source=eEPGCache::SCHEDULE_OTHER;
940                                 map=2;
941                                 break;
942                         default:
943                                 eDebug("[EPGC] unknown table_id !!!");
944                                 return;
945                 }
946                 tidMap &seenSections = this->seenSections[map];
947                 tidMap &calcedSections = this->calcedSections[map];
948                 if ( state == 1 && calcedSections == seenSections || state > 1 )
949                 {
950                         eDebugNoNewLine("[EPGC] ");
951                         switch (source)
952                         {
953                                 case eEPGCache::NOWNEXT:
954                                         m_NowNextConn=0;
955                                         eDebugNoNewLine("nownext");
956                                         break;
957                                 case eEPGCache::SCHEDULE:
958                                         m_ScheduleConn=0;
959                                         eDebugNoNewLine("schedule");
960                                         break;
961                                 case eEPGCache::SCHEDULE_OTHER:
962                                         m_ScheduleOtherConn=0;
963                                         eDebugNoNewLine("schedule other");
964                                         break;
965                                 default: eDebugNoNewLine("unknown");break;
966                         }
967                         eDebug(" finished(%d)", time(0)+eDVBLocalTimeHandler::getInstance()->difference());
968                         if ( reader )
969                                 reader->stop();
970                         isRunning &= ~source;
971                         if (!isRunning)
972                                 finishEPG();
973                 }
974                 else
975                 {
976                         eit_t *eit = (eit_t*) data;
977                         __u32 sectionNo = data[0] << 24;
978                         sectionNo |= data[3] << 16;
979                         sectionNo |= data[4] << 8;
980                         sectionNo |= eit->section_number;
981
982                         tidMap::iterator it =
983                                 seenSections.find(sectionNo);
984
985                         if ( it == seenSections.end() )
986                         {
987                                 seenSections.insert(sectionNo);
988                                 calcedSections.insert(sectionNo);
989                                 __u32 tmpval = sectionNo & 0xFFFFFF00;
990                                 __u8 incr = source == NOWNEXT ? 1 : 8;
991                                 for ( int i = 0; i <= eit->last_section_number; i+=incr )
992                                 {
993                                         if ( i == eit->section_number )
994                                         {
995                                                 for (int x=i; x <= eit->segment_last_section_number; ++x)
996                                                         calcedSections.insert(tmpval|(x&0xFF));
997                                         }
998                                         else
999                                                 calcedSections.insert(tmpval|(i&0xFF));
1000                                 }
1001                                 cache->sectionRead(data, source, this);
1002                         }
1003                 }
1004         }
1005 }
1006
1007 RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, const eventData *&result, int direction)
1008 // if t == -1 we search the current event...
1009 {
1010         singleLock s(cache_lock);
1011         uniqueEPGKey key(service);
1012
1013         // check if EPG for this service is ready...
1014         eventCache::iterator It = eventDB.find( key );
1015         if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached ?
1016         {
1017                 if (t==-1)
1018                         t = time(0)+eDVBLocalTimeHandler::getInstance()->difference();
1019                 timeMap::iterator i = direction <= 0 ? It->second.second.lower_bound(t) :  // find > or equal
1020                         It->second.second.upper_bound(t); // just >
1021                 if ( i != It->second.second.end() )
1022                 {
1023                         if ( direction < 0 || (direction == 0 && i->second->getStartTime() > t) )
1024                         {
1025                                 timeMap::iterator x = i;
1026                                 --x;
1027                                 if ( x != It->second.second.end() )
1028                                 {
1029                                         time_t start_time = x->second->getStartTime();
1030                                         if (direction >= 0)
1031                                         {
1032                                                 if (t < start_time)
1033                                                         return -1;
1034                                                 if (t > (start_time+x->second->getDuration()))
1035                                                         return -1;
1036                                         }
1037                                         i = x;
1038                                 }
1039                                 else
1040                                         return -1;
1041                         }
1042                         result = i->second;
1043                         return 0;
1044                 }
1045         }
1046         return -1;
1047 }
1048
1049 RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, const eit_event_struct *&result, int direction)
1050 {
1051         singleLock s(cache_lock);
1052         const eventData *data=0;
1053         RESULT ret = lookupEventTime(service, t, data, direction);
1054         if ( !ret && data )
1055                 result = data->get();
1056         return ret;
1057 }
1058
1059 RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, Event *& result, int direction)
1060 {
1061         singleLock s(cache_lock);
1062         const eventData *data=0;
1063         RESULT ret = lookupEventTime(service, t, data, direction);
1064         if ( !ret && data )
1065                 result = new Event((uint8_t*)data->get());
1066         return ret;
1067 }
1068
1069 RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, ePtr<eServiceEvent> &result, int direction)
1070 {
1071         singleLock s(cache_lock);
1072         const eventData *data=0;
1073         RESULT ret = lookupEventTime(service, t, data, direction);
1074         if ( !ret && data )
1075         {
1076                 Event ev((uint8_t*)data->get());
1077                 result = new eServiceEvent();
1078                 const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)service;
1079                 ret = result->parseFrom(&ev, (ref.getTransportStreamID().get()<<16)|ref.getOriginalNetworkID().get());
1080         }
1081         return ret;
1082 }
1083
1084 RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, const eventData *&result )
1085 {
1086         singleLock s(cache_lock);
1087         uniqueEPGKey key( service );
1088
1089         eventCache::iterator It = eventDB.find( key );
1090         if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached?
1091         {
1092                 eventMap::iterator i( It->second.first.find( event_id ));
1093                 if ( i != It->second.first.end() )
1094                 {
1095                         result = i->second;
1096                         return 0;
1097                 }
1098                 else
1099                 {
1100                         result = 0;
1101                         eDebug("event %04x not found in epgcache", event_id);
1102                 }
1103         }
1104         return -1;
1105 }
1106
1107 RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, const eit_event_struct *&result)
1108 {
1109         singleLock s(cache_lock);
1110         const eventData *data=0;
1111         RESULT ret = lookupEventId(service, event_id, data);
1112         if ( !ret && data )
1113                 result = data->get();
1114         return ret;
1115 }
1116
1117 RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, Event *& result)
1118 {
1119         singleLock s(cache_lock);
1120         const eventData *data=0;
1121         RESULT ret = lookupEventId(service, event_id, data);
1122         if ( !ret && data )
1123                 result = new Event((uint8_t*)data->get());
1124         return ret;
1125 }
1126
1127 RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, ePtr<eServiceEvent> &result)
1128 {
1129         singleLock s(cache_lock);
1130         const eventData *data=0;
1131         RESULT ret = lookupEventId(service, event_id, data);
1132         if ( !ret && data )
1133         {
1134                 Event ev((uint8_t*)data->get());
1135                 result = new eServiceEvent();
1136                 const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)service;
1137                 ret = result->parseFrom(&ev, (ref.getTransportStreamID().get()<<16)|ref.getOriginalNetworkID().get());
1138         }
1139         return ret;
1140 }
1141
1142 RESULT eEPGCache::startTimeQuery(const eServiceReference &service, time_t begin, int minutes)
1143 {
1144         eventCache::iterator It = eventDB.find( service );
1145         if ( It != eventDB.end() && It->second.second.size() )
1146         {
1147                 m_timemap_end = minutes != -1 ? It->second.second.upper_bound(begin+minutes*60) : It->second.second.end();
1148                 if ( begin != -1 )
1149                 {
1150                         m_timemap_cursor = It->second.second.lower_bound(begin);
1151                         if ( m_timemap_cursor != It->second.second.end() )
1152                         {
1153                                 if ( m_timemap_cursor->second->getStartTime() != begin )
1154                                 {
1155                                         timeMap::iterator x = m_timemap_cursor;
1156                                         --x;
1157                                         if ( x != It->second.second.end() )
1158                                         {
1159                                                 time_t start_time = x->second->getStartTime();
1160                                                 if ( begin > start_time && begin < (start_time+x->second->getDuration()))
1161                                                         m_timemap_cursor = x;
1162                                         }
1163                                 }
1164                         }
1165                 }
1166                 else
1167                         m_timemap_cursor = It->second.second.begin();
1168                 const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)service;
1169                 currentQueryTsidOnid = (ref.getTransportStreamID().get()<<16) | ref.getOriginalNetworkID().get();
1170                 return 0;
1171         }
1172         return -1;
1173 }
1174
1175 RESULT eEPGCache::getNextTimeEntry(const eventData *& result)
1176 {
1177         if ( m_timemap_cursor != m_timemap_end )
1178         {
1179                 result = m_timemap_cursor++->second;
1180                 return 0;
1181         }
1182         return -1;
1183 }
1184
1185 RESULT eEPGCache::getNextTimeEntry(const eit_event_struct *&result)
1186 {
1187         if ( m_timemap_cursor != m_timemap_end )
1188         {
1189                 result = m_timemap_cursor++->second->get();
1190                 return 0;
1191         }
1192         return -1;
1193 }
1194
1195 RESULT eEPGCache::getNextTimeEntry(Event *&result)
1196 {
1197         if ( m_timemap_cursor != m_timemap_end )
1198         {
1199                 result = new Event((uint8_t*)m_timemap_cursor++->second->get());
1200                 return 0;
1201         }
1202         return -1;
1203 }
1204
1205 RESULT eEPGCache::getNextTimeEntry(ePtr<eServiceEvent> &result)
1206 {
1207         if ( m_timemap_cursor != m_timemap_end )
1208         {
1209                 Event ev((uint8_t*)m_timemap_cursor++->second->get());
1210                 result = new eServiceEvent();
1211                 return result->parseFrom(&ev, currentQueryTsidOnid);
1212         }
1213         return -1;
1214 }
1215
1216 void fillTuple(PyObject *tuple, char *argstring, int argcount, PyObject *service, ePtr<eServiceEvent> &ptr, PyObject *nowTime, PyObject *service_name )
1217 {
1218         PyObject *tmp=NULL;
1219         int pos=0;
1220         while(pos < argcount)
1221         {
1222                 bool inc_refcount=false;
1223                 switch(argstring[pos])
1224                 {
1225                         case 'I': // Event Id
1226                                 tmp = ptr ? PyLong_FromLong(ptr->getEventId()) : NULL;
1227                                 break;
1228                         case 'B': // Event Begin Time
1229                                 tmp = ptr ? PyLong_FromLong(ptr->getBeginTime()) : NULL;
1230                                 break;
1231                         case 'D': // Event Duration
1232                                 tmp = ptr ? PyLong_FromLong(ptr->getDuration()) : NULL;
1233                                 break;
1234                         case 'T': // Event Title
1235                                 tmp = ptr ? PyString_FromString(ptr->getEventName().c_str()) : NULL;
1236                                 break;
1237                         case 'S': // Event Short Description
1238                                 tmp = ptr ? PyString_FromString(ptr->getShortDescription().c_str()) : NULL;
1239                                 break;
1240                         case 'E': // Event Extended Description
1241                                 tmp = ptr ? PyString_FromString(ptr->getExtendedDescription().c_str()) : NULL;
1242                                 break;
1243                         case 'C': // Current Time
1244                                 tmp = nowTime;
1245                                 inc_refcount = true;
1246                                 break;
1247                         case 'R': // service reference string
1248                                 tmp = service;
1249                                 inc_refcount = true;
1250                                 break;
1251                         case 'N': // service name
1252                                 tmp = service_name;
1253                                 inc_refcount = true;
1254                 }
1255                 if (!tmp)
1256                 {
1257                         tmp = Py_None;
1258                         inc_refcount = true;
1259                 }
1260                 if (inc_refcount)
1261                         Py_INCREF(tmp);
1262                 PyTuple_SET_ITEM(tuple, pos++, tmp);
1263         }
1264 }
1265
1266 PyObject *handleEvent(ePtr<eServiceEvent> &ptr, PyObject *dest_list, char* argstring, int argcount, PyObject *service, PyObject *nowTime, PyObject *service_name, PyObject *convertFunc, PyObject *convertFuncArgs)
1267 {
1268         if (convertFunc)
1269         {
1270                 fillTuple(convertFuncArgs, argstring, argcount, service, ptr, nowTime, service_name);
1271                 PyObject *result = PyObject_CallObject(convertFunc, convertFuncArgs);
1272                 if (result == NULL)
1273                 {
1274                         if (service_name)
1275                                 Py_DECREF(service_name);
1276                         if (nowTime)
1277                                 Py_DECREF(nowTime);
1278                         Py_DECREF(convertFuncArgs);
1279                         Py_DECREF(dest_list);
1280                         return result;
1281                 }
1282                 PyList_Append(dest_list, result);
1283                 Py_DECREF(result);
1284         }
1285         else
1286         {
1287                 PyObject *tuple = PyTuple_New(argcount);
1288                 fillTuple(tuple, argstring, argcount, service, ptr, nowTime, service_name);
1289                 PyList_Append(dest_list, tuple);
1290                 Py_DECREF(tuple);
1291         }
1292         return 0;
1293 }
1294
1295 // here we get a list with tuples
1296 // first tuple entry is the servicereference
1297 // the second is the type of query (0 = time, 1 = event_id)
1298 // the third
1299 //              when type is eventid it is the event_id
1300 //              when type is time then it is the start_time ( 0 for now_time )
1301 // the fourth is the end_time .. ( optional )
1302
1303 /* argv is a python string
1304    I = Event Id
1305    B = Event Begin Time
1306    D = Event Duration
1307    T = Event Title
1308    S = Event Short Description
1309    E = Event Extended Description
1310    C = Current Time
1311    R = Service Reference
1312    N = Service Name
1313 */
1314
1315 PyObject *eEPGCache::lookupEvent(PyObject *list, PyObject *convertFunc)
1316 {
1317         PyObject *convertFuncArgs=NULL;
1318         int argcount=0;
1319         char *argstring=NULL;
1320         if (!PyList_Check(list))
1321         {
1322                 PyErr_SetString(PyExc_StandardError,
1323                         "type error");
1324                 eDebug("no list");
1325                 return NULL;
1326         }
1327         int listIt=0;
1328         int listSize=PyList_Size(list);
1329         if (!listSize)
1330         {
1331                 PyErr_SetString(PyExc_StandardError,
1332                         "not params given");
1333                 eDebug("not params given");
1334                 return NULL;
1335         }
1336         else 
1337         {
1338                 PyObject *argv=PyList_GET_ITEM(list, 0); // borrowed reference!
1339                 if (PyString_Check(argv))
1340                 {
1341                         argstring = PyString_AS_STRING(argv);
1342                         ++listIt;
1343                 }
1344                 else
1345                         argstring = "I"; // just event id as default
1346                 argcount = strlen(argstring);
1347 //              eDebug("have %d args('%s')", argcount, argstring);
1348         }
1349         if (convertFunc)
1350         {
1351                 if (!PyCallable_Check(convertFunc))
1352                 {
1353                         PyErr_SetString(PyExc_StandardError,
1354                                 "convertFunc must be callable");
1355                         eDebug("convertFunc is not callable");
1356                         return NULL;
1357                 }
1358                 convertFuncArgs = PyTuple_New(argcount);
1359         }
1360
1361         PyObject *nowTime = strchr(argstring, 'C') ?
1362                 PyLong_FromLong(time(0)+eDVBLocalTimeHandler::getInstance()->difference()) :
1363                 NULL;
1364
1365         bool must_get_service_name = strchr(argstring, 'N') ? true : false;
1366
1367         // create dest list
1368         PyObject *dest_list=PyList_New(0);
1369         while(listSize > listIt)
1370         {
1371                 PyObject *item=PyList_GET_ITEM(list, listIt++); // borrowed reference!
1372                 if (PyTuple_Check(item))
1373                 {
1374                         int type=0;
1375                         long event_id=-1;
1376                         time_t stime=-1;
1377                         int minutes=0;
1378                         int tupleSize=PyTuple_Size(item);
1379                         int tupleIt=0;
1380                         PyObject *service=NULL;
1381                         while(tupleSize > tupleIt)  // parse query args
1382                         {
1383                                 PyObject *entry=PyTuple_GET_ITEM(item, tupleIt); // borrowed reference!
1384                                 switch(tupleIt++)
1385                                 {
1386                                         case 0:
1387                                         {
1388                                                 if (!PyString_Check(entry))
1389                                                 {
1390                                                         eDebug("tuple entry 0 is no a string");
1391                                                         continue;
1392                                                 }
1393                                                 service = entry;
1394                                                 break;
1395                                         }
1396                                         case 1:
1397                                                 type=PyInt_AsLong(entry);
1398                                                 if (type < -1 || type > 2)
1399                                                 {
1400                                                         eDebug("unknown type %d", type);
1401                                                         continue;
1402                                                 }
1403                                                 break;
1404                                         case 2:
1405                                                 event_id=stime=PyInt_AsLong(entry);
1406                                                 break;
1407                                         case 3:
1408                                                 minutes=PyInt_AsLong(entry);
1409                                                 break;
1410                                         default:
1411                                                 eDebug("unneeded extra argument");
1412                                                 break;
1413                                 }
1414                         }
1415                         eServiceReference ref(PyString_AS_STRING(service));
1416                         if (ref.type != eServiceReference::idDVB)
1417                         {
1418                                 eDebug("service reference for epg query is not valid");
1419                                 continue;
1420                         }
1421                         PyObject *service_name=NULL;
1422                         if (must_get_service_name)
1423                         {
1424                                 ePtr<iStaticServiceInformation> sptr;
1425                                 eServiceCenterPtr service_center;
1426                                 eServiceCenter::getPrivInstance(service_center);
1427                                 if (service_center)
1428                                 {
1429                                         service_center->info(ref, sptr);
1430                                         if (sptr)
1431                                         {
1432                                                 std::string name;
1433                                                 sptr->getName(ref, name);
1434                                                 if (name.length())
1435                                                         service_name = PyString_FromString(name.c_str());
1436                                         }
1437                                 }
1438                                 if (!service_name)
1439                                         service_name = PyString_FromString("<n/a>");
1440                         }
1441                         if (minutes)
1442                         {
1443                                 Lock();
1444                                 if (!startTimeQuery(ref, stime, minutes))
1445                                 {
1446                                         ePtr<eServiceEvent> ptr;
1447                                         while (!getNextTimeEntry(ptr))
1448                                         {
1449                                                 PyObject *ret = handleEvent(ptr, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs);
1450                                                 if (ret)
1451                                                         return ret;
1452                                         }
1453                                 }
1454                                 Unlock();
1455                         }
1456                         else
1457                         {
1458                                 ePtr<eServiceEvent> ptr;
1459                                 if (stime)
1460                                 {
1461                                         if (type == 2)
1462                                                 lookupEventId(ref, event_id, ptr);
1463                                         else
1464                                                 lookupEventTime(ref, stime, ptr, type);
1465                                 }
1466                                 PyObject *ret = handleEvent(ptr, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs);
1467                                 if (ret)
1468                                         return ret;
1469                         }
1470                         if (service_name)
1471                                 Py_DECREF(service_name);
1472                 }
1473         }
1474         if (convertFuncArgs)
1475                 Py_DECREF(convertFuncArgs);
1476         if (nowTime)
1477                 Py_DECREF(nowTime);
1478         return dest_list;
1479 }
1480
1481
1482
1483