/* Copyright (C) 2004-2011 Christian Wieninger This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The author can be reached at cwieninger@gmx.de The project's page is at http://winni.vdr-developer.org/epgsearch */ #include #include "epgsearchtools.h" #include "blacklist.h" #include "epgsearchcats.h" #include #include "menu_dirselect.h" #include "changrp.h" #include "menu_search.h" #include "menu_searchedit.h" #include "menu_searchresults.h" #include cBlacklists Blacklists; // -- cBlacklist ----------------------------------------------------------------- char *cBlacklist::buffer = NULL; cBlacklist::cBlacklist(void) { ID = -1; *search = 0; options = 1; useTime = false; startTime = 0000; stopTime = 2359; useChannel = false; channelMin = Channels.GetByNumber(cDevice::CurrentChannel()); channelMax = Channels.GetByNumber(cDevice::CurrentChannel()); channelGroup = NULL; useCase = false; mode = 0; useTitle = true; useSubtitle = true; useDescription = true; useDuration = false; minDuration = 0; maxDuration = 130; useDayOfWeek = false; DayOfWeek = 0; buffer = NULL; isGlobal = 0; useExtEPGInfo = false; catvalues = (char**) malloc(SearchExtCats.Count() * sizeof(char*)); cSearchExtCat *SearchExtCat = SearchExtCats.First(); int index = 0; while (SearchExtCat) { catvalues[index] = (char*)malloc(MaxFileName); *catvalues[index] = 0; SearchExtCat = SearchExtCats.Next(SearchExtCat); index++; } ignoreMissingEPGCats = 0; fuzzyTolerance = 1; } cBlacklist::~cBlacklist(void) { if (buffer) { free(buffer); buffer = NULL; } if (catvalues) { cSearchExtCat *SearchExtCat = SearchExtCats.First(); int index = 0; while (SearchExtCat) { free(catvalues[index]); SearchExtCat = SearchExtCats.Next(SearchExtCat); index++; } free(catvalues); catvalues = NULL; } } cBlacklist& cBlacklist::operator= (const cBlacklist &Blacklist) { memcpy(this, &Blacklist, sizeof(*this)); cSearchExtCat *SearchExtCat = SearchExtCats.First(); int index = 0; while (SearchExtCat) { *catvalues[index] = 0; strcpy(catvalues[index], Blacklist.catvalues[index]); SearchExtCat = SearchExtCats.Next(SearchExtCat); index++; } return *this; } void cBlacklist::CopyFromTemplate(const cSearchExt* templ) { options = templ->options; useTime = templ->useTime; startTime = templ->startTime; stopTime = templ->stopTime; useChannel = templ->useChannel; useCase = templ->useCase; mode = templ->mode; useTitle = templ->useTitle; useSubtitle = templ->useSubtitle; useDescription = templ->useDescription; useDuration = templ->useDuration; minDuration = templ->minDuration; maxDuration = templ->maxDuration; useDayOfWeek = templ->useDayOfWeek; DayOfWeek = templ->DayOfWeek; useExtEPGInfo = templ->useExtEPGInfo; isGlobal = 0; cSearchExtCat *SearchExtCat = SearchExtCats.First(); int index = 0; while (SearchExtCat) { strcpy(catvalues[index], templ->catvalues[index]); SearchExtCat = SearchExtCats.Next(SearchExtCat); index++; } channelMin = templ->channelMin; channelMax = templ->channelMax; if (channelGroup) free(channelGroup); if (templ->channelGroup) channelGroup = strdup(templ->channelGroup); fuzzyTolerance = templ->fuzzyTolerance; ignoreMissingEPGCats = templ->ignoreMissingEPGCats; } bool cBlacklist::operator< (const cListObject &ListObject) { cBlacklist *BL = (cBlacklist *)&ListObject; return strcasecmp(search, BL->search) < 0; } const char *cBlacklist::ToText(void) { char tmp_Start[5] = ""; char tmp_Stop[5] = ""; char tmp_minDuration[5] = ""; char tmp_maxDuration[5] = ""; char* tmp_chanSel = NULL; char* tmp_search = NULL; char* tmp_catvalues = NULL; free(buffer); tmp_search = strdup(search); while(strstr(tmp_search, "|")) tmp_search = strreplace(tmp_search, "|", "!^pipe^!"); // ugly: replace a pipe with something, that should not happen to be part of a regular expression strreplace(tmp_search, ':', '|'); if (useTime) { sprintf(tmp_Start, "%04d", startTime); sprintf(tmp_Stop, "%04d", stopTime); } if (useDuration) { sprintf(tmp_minDuration, "%04d", minDuration); sprintf(tmp_maxDuration, "%04d", maxDuration); } if (useChannel==1) { if (channelMin->Number() < channelMax->Number()) msprintf(&tmp_chanSel, "%s|%s", CHANNELSTRING(channelMin), CHANNELSTRING(channelMax)); else msprintf(&tmp_chanSel, "%s", CHANNELSTRING(channelMin)); } if (useChannel==2) { int channelGroupNr = ChannelGroups.GetIndex(channelGroup); if (channelGroupNr == -1) { LogFile.eSysLog("channel group %s does not exist!", channelGroup); useChannel = 0; } else tmp_chanSel = strdup(channelGroup); } if (useExtEPGInfo) { cSearchExtCat *SearchExtCat = SearchExtCats.First(); int index = 0; while (SearchExtCat) { char* catvalue = NULL; msprintf(&catvalue, "%s", catvalues[index]); while(strstr(catvalue, ":")) catvalue = strreplace(catvalue, ":", "!^colon^!"); // ugly: replace with something, that should not happen to be part ofa category value while(strstr(catvalue, "|")) catvalue = strreplace(catvalue, "|", "!^pipe^!"); // ugly: replace with something, that should not happen to be part of a regular expression if (index == 0) msprintf(&tmp_catvalues, "%d#%s", SearchExtCat->id, catvalue); else { char* temp = tmp_catvalues; msprintf(&tmp_catvalues, "%s|%d#%s", tmp_catvalues, SearchExtCat->id, catvalue); free(temp); } SearchExtCat = SearchExtCats.Next(SearchExtCat); index++; free(catvalue); } } msprintf(&buffer, "%d:%s:%d:%s:%s:%d:%s:%d:%d:%d:%d:%d:%d:%s:%s:%d:%d:%d:%s:%d:%d:%d", ID, tmp_search, useTime, tmp_Start, tmp_Stop, useChannel, (useChannel>0 && useChannel<3)?tmp_chanSel:"0", useCase, mode, useTitle, useSubtitle, useDescription, useDuration, tmp_minDuration, tmp_maxDuration, useDayOfWeek, DayOfWeek, useExtEPGInfo, useExtEPGInfo?tmp_catvalues:"", fuzzyTolerance, ignoreMissingEPGCats, isGlobal); if (tmp_chanSel) free(tmp_chanSel); if (tmp_search) free(tmp_search); if (tmp_catvalues) free(tmp_catvalues); return buffer; } bool cBlacklist::Parse(const char *s) { char *line; char *pos; char *pos_next; int parameter = 1; int valuelen; char value[MaxFileName]; pos = line = strdup(s); pos_next = pos + strlen(pos); if (*pos_next == '\n') *pos_next = 0; while (*pos) { while (*pos == ' ') pos++; if (*pos) { if (*pos != ':') { pos_next = strchr(pos, ':'); if (!pos_next) pos_next = pos + strlen(pos); valuelen = pos_next - pos + 1; if (valuelen > MaxFileName) valuelen = MaxFileName; strn0cpy(value, pos, valuelen); pos = pos_next; switch (parameter) { case 1: ID = atoi(value); break; case 2: strcpy(search, value); break; case 3: useTime = atoi(value); break; case 4: startTime = atoi(value); break; case 5: stopTime = atoi(value); break; case 6: useChannel = atoi(value); break; case 7: if (useChannel == 0) { channelMin = NULL; channelMax = NULL; } else if (useChannel == 1) { int minNum=0, maxNum=0; int fields = sscanf(value, "%d-%d", &minNum, &maxNum); if (fields == 0) // stored with ID { #ifdef __FreeBSD__ char *channelMinbuffer = MALLOC(char, 32); char *channelMaxbuffer = MALLOC(char, 32); int channels = sscanf(value, "%31[^|]|%31[^|]", channelMinbuffer, channelMaxbuffer); #else char *channelMinbuffer = NULL; char *channelMaxbuffer = NULL; int channels = sscanf(value, "%a[^|]|%a[^|]", &channelMinbuffer, &channelMaxbuffer); #endif channelMin = Channels.GetByChannelID(tChannelID::FromString(channelMinbuffer), true, true); if (!channelMin) { LogFile.eSysLog("ERROR: channel %s not defined", channelMinbuffer); channelMin = channelMax = NULL; useChannel = 0; } if (channels == 1) channelMax = channelMin; else { channelMax = Channels.GetByChannelID(tChannelID::FromString(channelMaxbuffer), true, true); if (!channelMax) { LogFile.eSysLog("ERROR: channel %s not defined", channelMaxbuffer); channelMin = channelMax = NULL; useChannel = 0; } } free(channelMinbuffer); free(channelMaxbuffer); } } else if (useChannel == 2) channelGroup = strdup(value); break; case 8: useCase = atoi(value); break; case 9: mode = atoi(value); break; case 10: useTitle = atoi(value); break; case 11: useSubtitle = atoi(value); break; case 12: useDescription = atoi(value); break; case 13: useDuration = atoi(value); break; case 14: minDuration = atoi(value); break; case 15: maxDuration = atoi(value); break; case 16: useDayOfWeek = atoi(value); break; case 17: DayOfWeek = atoi(value); break; case 18: useExtEPGInfo = atoi(value); break; case 19: if (!ParseExtEPGValues(value)) { LogFile.eSysLog("ERROR reading ext. EPG values - 1"); free(line); return false; } break; case 20: fuzzyTolerance = atoi(value); break; case 21: ignoreMissingEPGCats = atoi(value); break; case 22: isGlobal = atoi(value); break; default: break; } //switch } parameter++; } if (*pos) pos++; } //while strreplace(search, '|', ':'); while(strstr(search, "!^pipe^!")) strreplace(search, "!^pipe^!", "|"); free(line); return (parameter >= 19) ? true : false; } bool cBlacklist::ParseExtEPGValues(const char *s) { char *line; char *pos; char *pos_next; int valuelen; char value[MaxFileName]; pos = line = strdup(s); pos_next = pos + strlen(pos); if (*pos_next == '\n') *pos_next = 0; while (*pos) { while (*pos == ' ') pos++; if (*pos) { if (*pos != '|') { pos_next = strchr(pos, '|'); if (!pos_next) pos_next = pos + strlen(pos); valuelen = pos_next - pos + 1; if (valuelen > MaxFileName) valuelen = MaxFileName; strn0cpy(value, pos, valuelen); pos = pos_next; if (!ParseExtEPGEntry(value)) { LogFile.eSysLog("ERROR reading ext. EPG value: %s", value); free(line); return false; } } } if (*pos) pos++; } //while free(line); return true; } bool cBlacklist::ParseExtEPGEntry(const char *s) { char *line; char *pos; char *pos_next; int parameter = 1; int valuelen; char value[MaxFileName]; int currentid = -1; pos = line = strdup(s); pos_next = pos + strlen(pos); if (*pos_next == '\n') *pos_next = 0; while (*pos) { while (*pos == ' ') pos++; if (*pos) { if (*pos != '#') { pos_next = strchr(pos, '#'); if (!pos_next) pos_next = pos + strlen(pos); valuelen = pos_next - pos + 1; if (valuelen > MaxFileName) valuelen = MaxFileName; strn0cpy(value, pos, valuelen); pos = pos_next; switch (parameter) { case 1: { currentid = atoi(value); int index = SearchExtCats.GetIndexFromID(currentid); if (index > -1) strcpy(catvalues[index], ""); } break; case 2: if (currentid > -1) { int index = SearchExtCats.GetIndexFromID(currentid); if (index > -1) { while(strstr(value, "!^colon^!")) strreplace(value, "!^colon^!", ":"); while(strstr(value, "!^pipe^!")) strreplace(value, "!^pipe^!", "|"); strcpy(catvalues[index], value); } } break; default: break; } //switch } parameter++; } if (*pos) pos++; } //while free(line); return (parameter >= 2) ? true : false; } bool cBlacklist::Save(FILE *f) { return fprintf(f, "%s\n", ToText()) > 0; } cEvent * cBlacklist::GetEventByBlacklist(const cSchedule *schedules, const cEvent *Start, int MarginStop) { cEvent *pe = NULL; cEvent *p1 = NULL; if (Start) p1 = schedules->Events()->Next(Start); else p1 = schedules->Events()->First(); time_t tNow=time(NULL); char* szTest = NULL; char* searchText = strdup(search); if (!useCase) ToLower(searchText); int searchStart = 0, searchStop = 0; if (useTime) { searchStart = startTime; searchStop = stopTime; if (searchStop < searchStart) searchStop += 2400; } int minSearchDuration = 0; int maxSearchDuration = 0; if (useDuration) { minSearchDuration = minDuration/100*60 + minDuration%100; maxSearchDuration = maxDuration/100*60 + maxDuration%100; } for (cEvent *p = p1; p; p = schedules->Events()->Next(p)) { if(!p) { break; } if (szTest) { free(szTest); szTest = NULL; } // ignore events without title if (!p->Title() || strlen(p->Title()) == 0) continue; msprintf(&szTest, "%s%s%s%s%s", (useTitle?p->Title():""), (useSubtitle||useDescription)?"~":"", (useSubtitle?p->ShortText():""),useDescription?"~":"", (useDescription?p->Description():"")); if (tNow < p->EndTime() + MarginStop * 60) { if (!useCase) ToLower(szTest); if (useTime) { time_t tEvent = p->StartTime(); struct tm tmEvent; localtime_r(&tEvent, &tmEvent); int eventStart = tmEvent.tm_hour*100 + tmEvent.tm_min; int eventStart2 = tmEvent.tm_hour*100 + tmEvent.tm_min + 2400; if ((eventStart < searchStart || eventStart > searchStop) && (eventStart2 < searchStart || eventStart2 > searchStop)) continue; } if (useDuration) { int duration = p->Duration()/60; if (minSearchDuration > duration || maxSearchDuration < duration) continue; } if (useDayOfWeek) { time_t tEvent = p->StartTime(); struct tm tmEvent; localtime_r(&tEvent, &tmEvent); if (DayOfWeek >= 0 && DayOfWeek != tmEvent.tm_wday) continue; if (DayOfWeek < 0) { int iFound = 0; for(int i=0; i<7; i++) if (abs(DayOfWeek) & (int)pow(2,i) && i == tmEvent.tm_wday) { iFound = 1; break; } if (!iFound) continue; } } if (strlen(szTest) > 0) { if (!MatchesSearchMode(szTest, searchText, mode," ,;|~", fuzzyTolerance)) continue; } if (useExtEPGInfo && !MatchesExtEPGInfo(p)) continue; pe=p; break; } } if (szTest) free(szTest); free(searchText); return pe; } // returns a pointer array to the matching search results cSearchResults* cBlacklist::Run(cSearchResults* pSearchResults, int MarginStop) { LogFile.Log(3,"start search for blacklist '%s'", search); cSchedulesLock schedulesLock; const cSchedules *schedules; schedules = cSchedules::Schedules(schedulesLock); if(!schedules) { LogFile.Log(1,"schedules are currently locked! try again later."); return NULL; } const cSchedule *Schedule = schedules->First(); while (Schedule) { cChannel* channel = Channels.GetByChannelID(Schedule->ChannelID(),true,true); if (!channel) { Schedule = (const cSchedule *)schedules->Next(Schedule); continue; } if (useChannel == 1 && channelMin && channelMax) { if (channelMin->Number() > channel->Number() || channelMax->Number() < channel->Number()) { Schedule = (const cSchedule *)schedules->Next(Schedule); continue; } } if (useChannel == 2 && channelGroup) { cChannelGroup* group = ChannelGroups.GetGroupByName(channelGroup); if (!group || !group->ChannelInGroup(channel)) { Schedule = (const cSchedule *)schedules->Next(Schedule); continue; } } if (useChannel == 3) { if (channel->Ca() >= CA_ENCRYPTED_MIN) { Schedule = (const cSchedule *)schedules->Next(Schedule); continue; } } const cEvent *pPrevEvent = NULL; do { const cEvent* event = GetEventByBlacklist(Schedule, pPrevEvent, MarginStop); pPrevEvent = event; if (event && Channels.GetByChannelID(event->ChannelID(),true,true)) { if (!pSearchResults) pSearchResults = new cSearchResults; pSearchResults->Add(new cSearchResult(event, this)); } } while(pPrevEvent); Schedule = (const cSchedule *)schedules->Next(Schedule); } LogFile.Log(3,"found %d event(s) for blacklist '%s'", pSearchResults?pSearchResults->Count():0, search); return pSearchResults; } bool cBlacklist::MatchesExtEPGInfo(const cEvent* e) { if (!e || !e->Description()) return false; cSearchExtCat* SearchExtCat = SearchExtCats.First(); while (SearchExtCat) { char* value = NULL; int index = SearchExtCats.GetIndexFromID(SearchExtCat->id); if (index > -1) value = catvalues[index]; if (value && strlen(value) > 0) { char* testvalue = GetExtEPGValue(e, SearchExtCat); if (!testvalue) return false; // compare not case sensitive char* valueLower = strdup(value); ToLower(valueLower); ToLower(testvalue); if (!MatchesSearchMode(testvalue, valueLower, SearchExtCat->searchmode, ",;|~", fuzzyTolerance)) { free(testvalue); free(valueLower); return false; } free(testvalue); free(valueLower); } SearchExtCat = SearchExtCats.Next(SearchExtCat); } return true; } // -- cBlacklists ---------------------------------------------------------------- int cBlacklists::GetNewID() { int newID = -1; cMutexLock BlacklistLock(this); cBlacklist *l = (cBlacklist *)First(); while (l) { newID = max(newID, l->ID); l = (cBlacklist *)l->Next(); } return newID+1; } cBlacklist* cBlacklists::GetBlacklistFromID(int ID) { if (ID == -1) return NULL; cMutexLock BlacklistLock(this); cBlacklist *l = (cBlacklist *)First(); while (l) { if (l->ID == ID) return l; l = (cBlacklist *)l->Next(); } return NULL; } bool cBlacklists::Exists(const cBlacklist* Blacklist) { cMutexLock BlacklistLock(this); cBlacklist *l = (cBlacklist*)First(); while (l) { if (l == Blacklist) return true; l = (cBlacklist*)l->Next(); } return false; }