Implement threadsafe banlist and make maintenance of bans cleaner
authorclinton <clinton@unknownlamer.org>
Tue, 18 Nov 2008 23:16:18 +0000 (23:16 +0000)
committerclinton <clinton@unknownlamer.org>
Tue, 18 Nov 2008 23:16:18 +0000 (23:16 +0000)
* 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

12 files changed:
source/BanList.C [new file with mode: 0644]
source/BanList.H [new file with mode: 0644]
source/Bot.C
source/Bot.H
source/Channel.C
source/Channel.H
source/Commands.C
source/Makefile.am
source/Parser.C
source/TodoList.C [deleted file]
source/TodoList.H [deleted file]
source/UserCommands.C

diff --git a/source/BanList.C b/source/BanList.C
new file mode 100644 (file)
index 0000000..c6b6da8
--- /dev/null
@@ -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 <algorithm>
+
+#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 (file)
index 0000000..0bdb878
--- /dev/null
@@ -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 <algorithm>
+#include <functional>
+#include <list>
+#include <string>
+
+#include <ctime>
+
+#include "BotThreading.H"
+#include "Mask.H"
+
+class BanEntry;
+
+class BanList
+{
+public:
+  typedef std::list<BanEntry *> List;
+  typedef std::list<Mask> 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<typename T>
+  void foreach (const T & fun)
+  {
+    BotLock ban_lock (ban_mutex);
+    std::for_each (storage.begin (), storage.end (), fun);
+  }
+};
+
+#endif
index f479d81..e46a87e 100644 (file)
@@ -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<String, Channel *, std::less<String> >::iterator it = channelList->begin ();
+        it != channelList->end ();
+        ++it)
+      {
+       it->second->purge_expired_bans ();
+      }
 #ifdef USESCRIPTS
     botInterp->RunTimers(currentTime);
 
index 7dd25a1..ceb9a12 100644 (file)
@@ -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
index 5d35728..c0ab590 100644 (file)
@@ -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<BanEntry *>::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<BanEntry *>::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<BanEntry *>::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);
+         
+       }
     }
 }
index 348b943..40caad9 100644 (file)
@@ -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
 #ifndef CHANNEL_H
 #define CHANNEL_H
 
-#include <ctime>
+#include <algorithm>
+#include <functional>
 #include <map>
 #include <vector>
 
+#include <ctime>
+
+#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<String, User *, std::less<String> > channelMemory;
-  std::vector<BanEntry *> 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<typename T>  void for_each_ban_entry (const T & fun)
+  { channelBanlist.foreach (fun); }
   
   void parseMode(Person *, String);
   void resynchModes();
index ede6428..93b7676 100644 (file)
@@ -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<BanEntry *>::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<BanEntry *>::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<BanEntry *>::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;
 }
index 420a225..a13b420 100644 (file)
@@ -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 \
index 456cf0e..e7e1158 100644 (file)
@@ -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 (file)
index 099dedf..0000000
+++ /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<TodoListItem, std::less<TodoListItem> >::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 (file)
index 8450680..0000000
+++ /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 <functional>
-#include <set>
-
-#include <ctime>
-
-#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<TodoListItem, std::less<TodoListItem> > todoQueue;
-
-public:
-  TodoList();
-  ~TodoList();
-
-  void addDeban(String, String, std::time_t);
-
-  String getNext();
-};
-
-#endif
index 0ad6ba7..c60b91e 100644 (file)
@@ -24,6 +24,7 @@
 #include "UserCommands.H"
 
 #include <fstream>
+#include <functional>
 #include <map>
 #include <string>
 
@@ -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<BanEntry *>::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<UserListItem *>::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<BanEntry *>::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