summaryrefslogtreecommitdiff
path: root/blacklist.c
diff options
context:
space:
mode:
authorChristian Wieninger <winni@debian.(none)>2007-11-11 15:40:28 +0100
committerChristian Wieninger <winni@debian.(none)>2007-11-11 15:40:28 +0100
commit8d4f8607dc1558ce73eb4c376bdbf78ddb65da83 (patch)
treed0c5dde81a36ab2e8a2edc7c1e6922556518b312 /blacklist.c
downloadvdr-plugin-epgsearch-8d4f8607dc1558ce73eb4c376bdbf78ddb65da83.tar.gz
vdr-plugin-epgsearch-8d4f8607dc1558ce73eb4c376bdbf78ddb65da83.tar.bz2
Initial commit
Diffstat (limited to 'blacklist.c')
-rw-r--r--blacklist.c762
1 files changed, 762 insertions, 0 deletions
diff --git a/blacklist.c b/blacklist.c
new file mode 100644
index 0000000..aff3211
--- /dev/null
+++ b/blacklist.c
@@ -0,0 +1,762 @@
+/*
+Copyright (C) 2004-2007 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 <vector>
+#include "epgsearchtools.h"
+#include "blacklist.h"
+#include "epgsearchcats.h"
+#include <vdr/tools.h>
+#include "menu_dirselect.h"
+#include "changrp.h"
+#include "menu_search.h"
+#include "menu_searchedit.h"
+#include "menu_searchresults.h"
+#include <math.h>
+#ifdef HAVE_PCREPOSIX
+#include <pcreposix.h>
+#else
+#include <regex.h>
+#endif
+
+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;
+
+ 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));
+ catvalues = (char**) malloc(SearchExtCats.Count() * sizeof(char*));
+ cSearchExtCat *SearchExtCat = SearchExtCats.First();
+ int index = 0;
+ while (SearchExtCat)
+ {
+ catvalues[index] = (char*)malloc(MaxFileName);
+ *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;
+
+ 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);
+ if (mode < 4 || mode == 5) // not a regular expression
+ asprintf(&tmp_search, "%s", search);
+ else if (mode ==4)
+ {
+ tmp_search = strdup(search);
+ while(strstr(tmp_search, "|"))
+ tmp_search = strreplace(tmp_search, "|", "!^pipe^!"); // ugly: replace 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())
+ asprintf(&tmp_chanSel, "%s|%s", CHANNELSTRING(channelMin), CHANNELSTRING(channelMax));
+ else
+ asprintf(&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;
+ asprintf(&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)
+ asprintf(&tmp_catvalues, "%d#%s", SearchExtCat->id, catvalue);
+ else
+ {
+ char* temp = tmp_catvalues;
+ asprintf(&tmp_catvalues, "%s|%d#%s", tmp_catvalues, SearchExtCat->id, catvalue);
+ free(temp);
+ }
+ SearchExtCat = SearchExtCats.Next(SearchExtCat);
+ index++;
+ free(catvalue);
+ }
+ }
+
+ asprintf(&buffer, "%d:%s:%d:%s:%s:%d:%s:%d:%d:%d:%d:%d:%d:%s:%s:%d:%d:%d:%s:%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);
+
+
+ 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
+ {
+ char *channelMinbuffer = NULL;
+ char *channelMaxbuffer = NULL;
+ int channels = sscanf(value, "%a[^|]|%a[^|]", &channelMinbuffer, &channelMaxbuffer);
+ 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;
+ } //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;
+ } //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;
+
+ asprintf(&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;
+}