[Implement threadsafe banlist and make maintenance of bans cleaner clinton@unknownlamer.org**20081118231618 * BanList is now a separate class instead of being part of the channel * Channels now expire their own bans eliminating the TodoList class * UserCommands::TBan converted to use Commands::TBan * Channel::addBan/delBan now handles sending channel modes eliminating some duplicated code * All users of the channelBanlist have been updated to use the new interface ] { addfile ./source/BanList.C hunk ./source/BanList.C 1 +// BanList.C -*- C++ -*- +// Copyright (C) 2008 Clinton Ebadi + +// 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 +// 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. + + +#include "BanList.H" + +#include + +#include "BanEntry.H" +#include "Mask.H" + +BanList::BanList () +{ } + +BanList::~BanList () +{ + BotLock ban_lock (ban_mutex); + + while (!storage.empty ()) + { + delete storage.front (); + storage.pop_front (); + } +} + +bool BanList::add (const Mask & mask, std::time_t expiration) +{ + BotLock ban_lock (ban_mutex); + iterator it = find (mask); + + if (it != storage.end ()) + { + if (expiration > (*it)->getExpirationDate()) + (*it)->setExpirationDate (expiration); + + return false; + } + else + { + storage.push_back (new BanEntry (mask, expiration)); + + return true; + } +} + +bool BanList::del (const Mask & mask) +{ + BotLock ban_lock (ban_mutex); + iterator it = find (mask); + + if (it != storage.end ()) + { + BanEntry *b = *it; + storage.erase (it); + delete b; + + return true; + } + return false; +} + +namespace +{ + struct BanEntryFind + { + Mask mask; + + BanEntryFind (const Mask &m) + : mask (m) + { } + + bool operator() (const BanEntry * ban_entry) const + { return mask.getMask() == ban_entry->getMask().getMask (); } + }; +} + +BanList::iterator BanList::find (const Mask & mask) +{ + BanEntryFind find_ban_entry (mask); + + return std::find_if (storage.begin (), storage.end (), find_ban_entry); +} + +BanList::MatchList BanList::find_matches (const Mask & mask) const +{ + BotLock ban_lock (ban_mutex); + MatchList matches; + + for (List::const_iterator it = storage.begin (); + it != storage.end (); + ++it) + { + Mask dupmask = (*it)->getMask (); + if (mask.matches (dupmask)) + { + matches.push_front (dupmask); + } + } + + return matches; +} + +BanList::MatchList BanList::delete_expired_x () +{ + BotLock ban_lock (ban_mutex); + std::time_t now = std::time (0); + MatchList expired; + + List::iterator it = storage.begin (); + while (it != storage.end ()) + { + if ((*it)->getExpirationDate () > -1 && (*it)->getExpirationDate () < now) + { + expired.push_front ((*it)->getMask ()); + delete *it; + storage.erase (it++); + } + else + { + ++it; + } + } + + return expired; +} addfile ./source/BanList.H hunk ./source/BanList.H 1 +// BanList.H -*- C++ -*- +// Copyright (C) 2008 Clinton Ebadi + +// 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 +// 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. + +#ifndef BANLIST_H +#define BANLIST_H + +#include +#include +#include +#include + +#include + +#include "BotThreading.H" +#include "Mask.H" + +class BanEntry; + +class BanList +{ +public: + typedef std::list List; + typedef std::list MatchList; + typedef List::iterator iterator; + +private: + List storage; + BotMutex ban_mutex; + + iterator find (const Mask&); + +public: + BanList (); + ~BanList (); + + bool add (const Mask&, std::time_t = -1); + bool del (const Mask&); + + MatchList find_matches (const Mask&) const; + MatchList delete_expired_x (); + + template + void foreach (const T & fun) + { + BotLock ban_lock (ban_mutex); + std::for_each (storage.begin (), storage.end (), fun); + } +}; + +#endif hunk ./source/Bot.C 51 -#include "TodoList.H" hunk ./source/Bot.C 130 - todoList = new TodoList(); hunk ./source/Bot.C 210 - delete todoList; hunk ./source/Bot.C 445 - String line; - while ((line = todoList->getNext()) != "") { - serverConnection->queue->sendChannelMode(line); - } + for (std::map >::iterator it = channelList->begin (); + it != channelList->end (); + ++it) + { + it->second->purge_expired_bans (); + } hunk ./source/Bot.H 48 -class TodoList; hunk ./source/Bot.H 85 - TodoList * todoList; hunk ./source/Channel.C 31 +#include "Mask.H" hunk ./source/Channel.C 53 - channelBanlist.clear(); hunk ./source/Channel.C 77 - - BanEntry *b; - std::vector::iterator it2; - - while (channelBanlist.size() != 0) { - it2 = channelBanlist.begin(); - b = *it2; - channelBanlist.erase(it2); - delete b; - } hunk ./source/Channel.C 166 -Channel::addBan(String mask, std::time_t expiration) +Channel::addBan(const Mask & mask, std::time_t expiration) hunk ./source/Channel.C 168 - for (std::vector::iterator it = channelBanlist.begin(); - it != channelBanlist.end(); ++it) - if ((*it)->getMask().getMask() == mask) { - if (expiration > (*it)->getExpirationDate()) - (*it)->setExpirationDate(expiration); - return; - } - channelBanlist.push_back(new BanEntry(mask, expiration)); + if (channelBanlist.add (mask, expiration)) + cnx->queue->sendChannelMode (channelName, "+b", mask.getMask ()); hunk ./source/Channel.C 173 -Channel::delBan(String mask) +Channel::delBan(const Mask & mask) hunk ./source/Channel.C 175 - for (std::vector::iterator it = channelBanlist.begin(); - it != channelBanlist.end(); ++it) - if (mask == (*it)->getMask().getMask()) { - BanEntry *b = *it; - channelBanlist.erase(it); - delete b; - break; + BanList::MatchList matches = channelBanlist.find_matches (mask); + + for (BanList::MatchList::const_iterator it = matches.begin (); + it != matches.end (); + ++it) + { + if (channelBanlist.del (*it)) + cnx->queue->sendChannelMode (channelName, "-b", mask.getMask ()); hunk ./source/Channel.C 186 +void Channel::purge_expired_bans () +{ + BanList::MatchList expired = channelBanlist.delete_expired_x (); + + for (BanList::MatchList::const_iterator it = expired.begin (); + it != expired.end (); + ++it) + cnx->queue->sendChannelMode (channelName, "-b", (*it).getMask ()); +} + hunk ./source/Channel.C 340 - sign == '+' ? addBan(m) : delBan(m); - if (sign == '-') { - ShitEntry * se = - cnx->bot->shitList->getShit(m, channelName); - if (se && se->isStillValid() && - se->getShitLevel() >= ShitEntry::SHIT_NODEBAN) + if (sign == '+') + channelBanlist.add (m); + if (sign == '-') + { + ShitEntry * se = cnx->bot->shitList->getShit(m, channelName); + + if (se && se->isStillValid() && + se->getShitLevel() >= ShitEntry::SHIT_NODEBAN) hunk ./source/Channel.C 349 - } + else + channelBanlist.del (m); + + } hunk ./source/Channel.H 3 -// Copyright (C) 2002 Clinton Ebadi +// Copyright (C) 2002,2008 Clinton Ebadi hunk ./source/Channel.H 23 -#include +#include +#include hunk ./source/Channel.H 28 +#include + +#include "BanList.H" hunk ./source/Channel.H 36 +class Mask; hunk ./source/Channel.H 76 - std::vector channelBanlist; + BanList channelBanlist; hunk ./source/Channel.H 101 - void addBan(String, std::time_t = -1); - void delBan(String); + void addBan(const Mask&, std::time_t = -1); + void delBan(const Mask&); + void purge_expired_bans (); + template void for_each_ban_entry (const T & fun) + { channelBanlist.foreach (fun); } hunk ./source/Commands.C 34 -#include "TodoList.H" hunk ./source/Commands.C 176 - - for (std::vector::iterator it = c->channelBanlist.begin(); - it != c->channelBanlist.end(); ++it) - if (m.matches((*it)->getMask ()) && (*it)->getMask().getMask() != m.getMask()) - QUEUE->sendChannelMode(channel, "-b", (*it)->getMask().getMask()); hunk ./source/Commands.C 177 - QUEUE->sendChannelMode(channel, "+b", dest); - + c->delBan (m); + c->addBan (m, -1); + hunk ./source/Commands.C 300 + + BanList::MatchList matches = c->channelBanlist.find_matches (m); hunk ./source/Commands.C 303 - for (std::vector::iterator it = c->channelBanlist.begin(); - it != c->channelBanlist.end(); ++it) - if (m.matches((*it)->getMask())) { - // Let's see if the ban is in the shitlist - ShitEntry *se = bot->shitList->getShit((*it)->getMask().getMask(), channel); - if (!se || !se->isStillValid() || - se->getShitLevel() < ShitEntry::SHIT_NODEBAN) - QUEUE->sendChannelMode(channel, "-b", (*it)->getMask().getMask()); - } + for (BanList::MatchList::iterator it = matches.begin (); + it != matches.end(); + ++it) + if (m.matches(*it)) + { + // Let's see if the ban is in the shitlist + ShitEntry *se = bot->shitList->getShit(it->getMask(), channel); + if (!se || !se->isStillValid() || + se->getShitLevel() < ShitEntry::SHIT_NODEBAN) + c->delBan (m); + } hunk ./source/Commands.C 780 - { - return NotOnChannel(channel); - } - + return NotOnChannel(channel); hunk ./source/Commands.C 782 - { - return NotChannelOp(channel); - } - + return NotChannelOp(channel); hunk ./source/Commands.C 784 - { - return InvalidTime(seconds); - } - + return InvalidTime(seconds); + hunk ./source/Commands.C 788 - { - dest = bot->getUserhost(channel, who); - } + dest = bot->getUserhost(channel, who); hunk ./source/Commands.C 790 - { - dest = who; - } - + dest = who; + hunk ./source/Commands.C 793 - { - return UserNotFound(who, channel); - } - + return UserNotFound(who, channel); hunk ./source/Commands.C 811 - // Clear existing bans on the user - for (std::vector::iterator it = c->channelBanlist.begin(); - it != c->channelBanlist.end(); ++it) - { - if (m.matches((*it)->getMask ())) - { - QUEUE->sendChannelMode(channel, "-b", (*it)->getMask().getMask()); - } - } hunk ./source/Commands.C 812 - // Ban them - CHANNEL(channel)->addBan(dest, seconds); - QUEUE->sendChannelMode(channel, "+b", dest); - bot->todoList->addDeban(channel, dest, seconds); + // c->delBan (dest); + c->addBan(dest, seconds); hunk ./source/Makefile.am 4 + BanList.C \ hunk ./source/Makefile.am 36 - TodoList.C \ hunk ./source/Makefile.am 41 + BanList.H \ hunk ./source/Makefile.am 70 - TodoList.H \ hunk ./source/Parser.C 175 - String parameters = st.rest (); - parameters = parameters.substr (1); - cnx->bot->userhostMap[num] = parameters; + cnx->bot->userhostMap[num] = Utils::trim_str (st.rest().substr(1)); hunk ./source/Parser.C 309 - c->addBan (st.next_token (), -1); + c->addBan (Mask(st.next_token ()), -1); hunk ./source/TodoList.C 1 -// TodoList.C -*- C++ -*- -// Copyright (c) 1998 Etienne BERNARD -// Copyright (C) 2002 Clinton Ebadi - -// 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. - -#include "TodoList.H" - -TodoList::TodoList() -{ -#ifdef HAVE_STL_CLEAR - todoQueue.clear(); -#endif -} - -TodoList::~TodoList() -{ } - -void -TodoList::addDeban(String channel, String mask, time_t when) -{ - TodoListItem tdli(String("MODE ") + channel + - " -b " + mask, when); - todoQueue.insert(tdli); -} - -String -TodoList::getNext() -{ - std::multiset >::iterator it; - std::time_t current_time = time(0); - - it = todoQueue.begin(); - - if (it == todoQueue.end() || (*it).when > current_time) - return ""; - - String result = (*it).line; - todoQueue.erase(it); - - return result; -} rmfile ./source/TodoList.C hunk ./source/TodoList.H 1 -// TodoList.H -*- C++ -*- -// Copyright (c) 1998 Etienne BERNARD -// Copyright (c) 2002 Clinton Ebadi - -// 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. - -#ifndef TODOLIST_H -#define TODOLIST_H - -#include -#include - -#include - -#include "String.H" - -class TodoList; - -class TodoListItem { - String line; - std::time_t when; -public: - TodoListItem(String l, std::time_t w) - : line(l), when(w) { } - - bool operator<(const TodoListItem &t) const - { return when < t.when; } - - friend class TodoList; -}; - -class TodoList { - - std::multiset > todoQueue; - -public: - TodoList(); - ~TodoList(); - - void addDeban(String, String, std::time_t); - - String getNext(); -}; - -#endif rmfile ./source/TodoList.H hunk ./source/UserCommands.C 27 +#include hunk ./source/UserCommands.C 70 -#include "TodoList.H" hunk ./source/UserCommands.C 321 +namespace +{ + void print_ban_list (Person * from, const BanEntry * b) + { + if (b->getExpirationDate() == -1) + from->sendNotice(b->getMask().getMask().pad(30) + " -1"); + else + from->sendNotice(b->getMask().getMask().pad(30) + " " + + String((long)(b->getExpirationDate() - std::time (0)))); + } +} + hunk ./source/UserCommands.C 337 - time_t current = time(0); hunk ./source/UserCommands.C 338 + hunk ./source/UserCommands.C 342 - for (std::vector::iterator it = c->channelBanlist.begin(); - it != c->channelBanlist.end(); ++it) - if ((*it)->getExpirationDate() == -1) - from->sendNotice((*it)->getMask().getMask().pad(30) + " -1"); - else - from->sendNotice((*it)->getMask().getMask().pad(30) + " " + - String((long)((*it)->getExpirationDate()-current))); + + c->for_each_ban_entry (std::bind1st (std::ptr_fun (print_ban_list), + from)); + hunk ./source/UserCommands.C 1340 -// FIXME: Convert hunk ./source/UserCommands.C 1345 - hunk ./source/UserCommands.C 1348 - String dest; hunk ./source/UserCommands.C 1365 - if (!cnx->bot->iAmOp(channel)) { - if (from) - from->sendNotice(String("\002I am not channel op on\002 ") + - channel); - return; - } - - if (!Utils::wildcard_p(who)) - dest = cnx->bot->getUserhost(channel, who); - else - dest = who; - - if (dest == "") { - if (from) - from->sendNotice(String("\002I can not find\002 ") + who); - return; - } - - time_t w; - - if ((w = Utils::str2time(t)) == 0) { - if (from) - from->sendNotice(t + " \002is an invalid time.\002"); - return; - } - - dest = Utils::make_wildcard(dest); - Mask m(dest); - - for (std::list::iterator it = cnx->bot->userList->l.begin(); - it != cnx->bot->userList->l.end(); - it++) - if (m.matches((*it)->mask) && - (*it)->channelMask.matches(channel) && - (*it)->prot >= User::NO_BAN) { - if (from) - from->sendNotice(String("\002I can not ban\002 ") + - who + " \002on channel\002 " + - channel + " \002(protection).\002"); - return; - } - - for (std::vector::iterator it = c->channelBanlist.begin(); - it != c->channelBanlist.end(); ++it) - if (m.matches((*it)->getMask())) - cnx->queue->sendChannelMode(channel, "-b", (*it)->getMask().getMask()); - - cnx->bot->channelList->getChannel(channel)->addBan(dest, w); - cnx->queue->sendChannelMode(channel, "+b", dest); - cnx->bot->todoList->addDeban(channel, dest, (time_t)w); + Message ret = Commands::TBan (cnx->bot, channel, who, Utils::str2time (t)); + if (ret.getCode () && from) + from->sendNotice ("\002" + ret.getMessage () + "\002"); }