From f9723c92f92fda0725a0fdbacd0bc37d529c9265 Mon Sep 17 00:00:00 2001 From: clinton Date: Tue, 18 Nov 2008 23:16:18 +0000 Subject: [PATCH] Implement threadsafe banlist and make maintenance of bans cleaner * 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 --- source/BanList.C | 140 ++++++++++++++++++++++++++++++++++++++++++ source/BanList.H | 65 ++++++++++++++++++++ source/Bot.C | 13 ++-- source/Bot.H | 2 - source/Channel.C | 70 ++++++++++----------- source/Channel.H | 18 ++++-- source/Commands.C | 79 ++++++++---------------- source/Makefile.am | 4 +- source/Parser.C | 6 +- source/TodoList.C | 54 ---------------- source/TodoList.H | 57 ----------------- source/UserCommands.C | 83 +++++++------------------ 12 files changed, 310 insertions(+), 281 deletions(-) create mode 100644 source/BanList.C create mode 100644 source/BanList.H delete mode 100644 source/TodoList.C delete mode 100644 source/TodoList.H diff --git a/source/BanList.C b/source/BanList.C new file mode 100644 index 0000000..c6b6da8 --- /dev/null +++ b/source/BanList.C @@ -0,0 +1,140 @@ +// 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; +} diff --git a/source/BanList.H b/source/BanList.H new file mode 100644 index 0000000..0bdb878 --- /dev/null +++ b/source/BanList.H @@ -0,0 +1,65 @@ +// 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 diff --git a/source/Bot.C b/source/Bot.C index f479d81..e46a87e 100644 --- a/source/Bot.C +++ b/source/Bot.C @@ -48,7 +48,6 @@ #include "ServerList.H" #include "ShitList.H" #include "StringTokenizer.H" -#include "TodoList.H" #include "User.H" #include "UserCommands.H" #include "UserList.H" @@ -128,7 +127,6 @@ Bot::Bot(String filename, bool debug_on) readConfig(); userList = new UserList(userListFileName); shitList = new ShitList(shitListFileName); - todoList = new TodoList(); dccConnections = new DCCManager (); // Let's read the alias file @@ -209,7 +207,6 @@ Bot::~Bot() shitList->save(); delete channelList; delete userList; - delete todoList; delete serverList; delete shitList; delete serverConnection; @@ -445,10 +442,12 @@ Bot::waitForInput() if ((*it).second > 0) (*it).second--; - 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 (); + } #ifdef USESCRIPTS botInterp->RunTimers(currentTime); diff --git a/source/Bot.H b/source/Bot.H index 7dd25a1..ceb9a12 100644 --- a/source/Bot.H +++ b/source/Bot.H @@ -45,7 +45,6 @@ class Person; class ServerConnection; class ServerList; class ShitList; -class TodoList; class UserList; class UserCommands; @@ -83,7 +82,6 @@ public: UserList * userList; ShitList * shitList; ServerList * serverList; - TodoList * todoList; #ifdef USESCRIPTS BotInterp * botInterp; #endif diff --git a/source/Channel.C b/source/Channel.C index 5d35728..c0ab590 100644 --- a/source/Channel.C +++ b/source/Channel.C @@ -28,6 +28,7 @@ #include "BanEntry.H" #include "Bot.H" #include "Macros.H" +#include "Mask.H" #include "Person.H" #include "ServerConnection.H" #include "ShitEntry.H" @@ -49,7 +50,6 @@ Channel::Channel(ServerConnection *c, { #ifdef HAVE_STL_CLEAR channelMemory.clear(); - channelBanlist.clear(); #endif if (c->bot->wantedChannels[channelName]) { @@ -74,16 +74,6 @@ Channel::~Channel() channelMemory.erase(it); delete u; } - - BanEntry *b; - std::vector::iterator it2; - - while (channelBanlist.size() != 0) { - it2 = channelBanlist.begin(); - b = *it2; - channelBanlist.erase(it2); - delete b; - } } void @@ -173,31 +163,36 @@ Channel::getUser(String n) } void -Channel::addBan(String mask, std::time_t expiration) +Channel::addBan(const Mask & mask, std::time_t expiration) { - 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 ()); } void -Channel::delBan(String mask) +Channel::delBan(const Mask & mask) { - 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 ()); } } +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 ()); +} + void Channel::resynchModes() { @@ -342,13 +337,18 @@ Channel::parseMode(Person *from, String mode) break; case 'b': String m = st.next_token(); - 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) cnx->queue->sendChannelMode(channelName, "+b", m); - } + else + channelBanlist.del (m); + + } } } diff --git a/source/Channel.H b/source/Channel.H index 348b943..40caad9 100644 --- a/source/Channel.H +++ b/source/Channel.H @@ -1,6 +1,6 @@ // Channel.H -*- C++ -*- // Copyright (c) 1997, 1998 Etienne BERNARD -// Copyright (C) 2002 Clinton Ebadi +// Copyright (C) 2002,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 @@ -20,15 +20,20 @@ #ifndef CHANNEL_H #define CHANNEL_H -#include +#include +#include #include #include +#include + +#include "BanList.H" #include "String.H" class BanEntry; class Bot; class Commands; +class Mask; class Parser; class Person; class ServerConnection; @@ -68,7 +73,7 @@ class Channel { bool doMode; bool gotWho; std::map > channelMemory; - std::vector channelBanlist; + BanList channelBanlist; ServerConnection * cnx; public: @@ -93,8 +98,11 @@ public: bool hasNick(String); User * getUser(String); - 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); } void parseMode(Person *, String); void resynchModes(); diff --git a/source/Commands.C b/source/Commands.C index ede6428..93b7676 100644 --- a/source/Commands.C +++ b/source/Commands.C @@ -31,7 +31,6 @@ #include "ShitEntry.H" #include "ShitList.H" #include "StringTokenizer.H" -#include "TodoList.H" #include "User.H" #include "UserList.H" #include "Utils.H" @@ -174,14 +173,10 @@ Commands::Ban(Bot *bot, String channel, String who) (*it)->channelMask.matches(channel) && (*it)->prot >= User::NO_BAN) return UserProtected(who, channel); - - 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()); - QUEUE->sendChannelMode(channel, "+b", dest); - + c->delBan (m); + c->addBan (m, -1); + return Ok; } @@ -302,16 +297,20 @@ Commands::Deban(Bot *bot, String channel, String who) dest = Utils::make_wildcard(dest); Mask m(dest); + + BanList::MatchList matches = c->channelBanlist.find_matches (m); - 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); + } return Ok; } @@ -778,35 +777,20 @@ Commands::TBan(Bot *bot, String channel, String who, int seconds) // Make sure all of the inputs are valid if (!c) - { - return NotOnChannel(channel); - } - + return NotOnChannel(channel); if (!bot->iAmOp(channel)) - { - return NotChannelOp(channel); - } - + return NotChannelOp(channel); if (seconds <= 0) - { - return InvalidTime(seconds); - } - + return InvalidTime(seconds); + // Look for user if (!Utils::wildcard_p(who)) - { - dest = bot->getUserhost(channel, who); - } + dest = bot->getUserhost(channel, who); else - { - dest = who; - } - + dest = who; + if (dest.length() == 0) - { - return UserNotFound(who, channel); - } - + return UserNotFound(who, channel); dest = Utils::make_wildcard(dest); Mask m(dest); @@ -824,20 +808,9 @@ Commands::TBan(Bot *bot, String channel, String who, int seconds) } } - // 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()); - } - } - // 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); return Ok; } diff --git a/source/Makefile.am b/source/Makefile.am index 420a225..a13b420 100644 --- a/source/Makefile.am +++ b/source/Makefile.am @@ -1,6 +1,7 @@ bin_PROGRAMS = bobotpp bobotpp_SOURCES = BanEntry.C \ + BanList.C \ Bot.C \ BotConfig.C \ BotInterp.C \ @@ -32,12 +33,12 @@ bobotpp_SOURCES = BanEntry.C \ Socket.C \ String.C \ StringTokenizer.C \ - TodoList.C \ User.C \ UserCommands.C \ UserList.C \ Utils.C \ BanEntry.H \ + BanList.H \ Bot.H \ BotConfig.H \ BotInterp.H \ @@ -66,7 +67,6 @@ bobotpp_SOURCES = BanEntry.C \ Socket.H \ String.H \ StringTokenizer.H \ - TodoList.H \ User.H \ UserCommands.H \ UserList.H \ diff --git a/source/Parser.C b/source/Parser.C index 456cf0e..e7e1158 100644 --- a/source/Parser.C +++ b/source/Parser.C @@ -172,9 +172,7 @@ Parser::parse302 (ServerConnection * cnx, Person * from, String rest) if (st.rest ().length ()) { st.next_token ('='); - String parameters = st.rest (); - parameters = parameters.substr (1); - cnx->bot->userhostMap[num] = parameters; + cnx->bot->userhostMap[num] = Utils::trim_str (st.rest().substr(1)); } else cnx->bot->userhostMap[num] = ""; @@ -308,7 +306,7 @@ Parser::parse367 (ServerConnection * cnx, Person * from, String rest) st.next_token (); String ch = st.next_token (); if (Channel * c = cnx->bot->channelList->getChannel (ch)) - c->addBan (st.next_token (), -1); + c->addBan (Mask(st.next_token ()), -1); } void diff --git a/source/TodoList.C b/source/TodoList.C deleted file mode 100644 index 099dedf..0000000 --- a/source/TodoList.C +++ /dev/null @@ -1,54 +0,0 @@ -// 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; -} diff --git a/source/TodoList.H b/source/TodoList.H deleted file mode 100644 index 8450680..0000000 --- a/source/TodoList.H +++ /dev/null @@ -1,57 +0,0 @@ -// 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 diff --git a/source/UserCommands.C b/source/UserCommands.C index 0ad6ba7..c60b91e 100644 --- a/source/UserCommands.C +++ b/source/UserCommands.C @@ -24,6 +24,7 @@ #include "UserCommands.H" #include +#include #include #include @@ -66,7 +67,6 @@ #include "ServerList.H" #include "ShitEntry.H" #include "StringTokenizer.H" -#include "TodoList.H" #include "User.H" #include "UserList.H" #include "Utils.H" @@ -318,22 +318,31 @@ UserCommands::Ban(ServerConnection *cnx, Person *from, from->sendNotice(m.getMessage()); } +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)))); + } +} + void UserCommands::BanList(ServerConnection *cnx, Person *from, String channel, String rest) { - time_t current = time(0); Channel *c = cnx->bot->channelList->getChannel(channel); + from->sendNotice(String("\002Banlist for channel\002 ") + channel + "\002:\002"); from->sendNotice("\002Mask Expires (seconds)\002"); - 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)); + from->sendNotice("\002End of banlist.\002"); } @@ -1328,17 +1337,14 @@ UserCommands::Stats(ServerConnection *cnx, Person *from, channel); } -// FIXME: Convert void UserCommands::TBan(ServerConnection *cnx, Person *from, String channel, String rest) { Channel * c = cnx->bot->channelList->getChannel(channel); - StringTokenizer st(rest); String who = st.next_token(); String t = st.next_token(); - String dest; if (who == "" || t == "") { if (from) @@ -1356,56 +1362,9 @@ UserCommands::TBan(ServerConnection *cnx, Person *from, } } - 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"); } // FIXME: Convert -- 2.20.1