Split user list from Channel into ChannelUserList
[clinton/bobotpp.git] / source / Commands.C
index 5d381ba..b1e4f5f 100644 (file)
@@ -1,6 +1,6 @@
 // Commands.C  -*- C++ -*-
 // Copyright (c) 1998 Etienne BERNARD
-// Copyright (C) 2002 Clinton Ebadi
+// Copyright (C) 2002,2005 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
 
 // 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
 
+#include "Commands.H"
+
+#include <functional>
+
+#include "BanEntry.H"
+#include "Bot.H"
+#include "ChannelList.H"
 #include "Macros.H"
 #include "Message.H"
-#include "Commands.H"
+#include "Server.H"
+#include "ServerConnection.H"
+#include "ServerList.H"
+#include "ServerQueueItem.H"
+#include "ShitEntry.H"
+#include "ShitList.H"
+#include "StringTokenizer.H"
+#include "User.H"
+#include "UserList.H"
 #include "Utils.H"
 
-#define Ok (Message(0, ""))
-#define NotOnChannel(c) (Message(-1, String("I am not on channel ") + (c)))
-#define NotChannelOp(c) (Message(-2, String("I am not channel op on ") + (c)))
-#define UserNotFound(w, c) (Message(-3, (w) + " is not on channel " + (c)))
-#define UserNotOp(w, c) (Message(-4, (w) + " is not channel op on " + (c)))
-#define UserProtected(w, c) (Message(-5, (w) + " is protected on " + (c)))
-#define InvalidNick(n) (Message(-6, (n) + " is not a valid nickname"))
-#define InvalidChannel(c) (Message(-7, (c) + " is not a valid channel name"))
-#define MassOpNotAllowed (Message(-8, "Mass op is not allowed."))
-#define UserOnShitList(w) (Message(-9, String("User ") + w + " is on my shitlist"))
-#define CanNotChangeTopic(c) (Message(-10, String("I can not change topic on ") + (c)))
-#define TopicLocked(c) (Message(-11, String("Topic is locked on ") + (c)))
-#define InvalidPort(p) (Message(-12, String((long)(p)) + " is an invalid port number"))
-#define InvalidTime(t) (Message(-13, String((long)(t)) + " is an invalid time"))
-#define CanNotChangeServer (Message(-14, "I cannot change server without loosing op on one of my channels"))
-#define EmptyServerList (Message(-15, "Server list is empty"))
-#define InvalidServerNumber(n) (Message(-16, String((long)(n)) + " is an invalid server number"))
-#define InvalidParameters (Message(-17, "Invalid parameters"))
-#define NotFound(w) (Message(-18, String("I can not find ") + (w)));
-#define NotInUserlist(w) (Message(-19, (w) + " is not in my userlist"))
-#define NotInShitlist(w) (Message(-20, (w) + " is not in my shitlist"))
-#define AlreadyInUserlist(m, mc) (Message(-21, (m) + " is already in userlist on channel(s) " + (mc)))
-#define AlreadyInShitlist(m, mc) (Message(-22, (m) + " is already in shitlist on channel(s) " + (mc)))
-#define EmptyMessage (Message(-23, "Can not send an empty message"))
-#define EmptyAddressee (Message(-24, "Can not send to nobody"))
-#define NotToChannel (Message(-25, "Can not send to a channel. Use \"say\" instead."))
-#define NotConnected (Message(-26, "Not connected."))
+#ifdef USESCRIPTS
+#include "BotInterp.H"
+#endif
+
 
 #define CHECK_CONNECTION if (!bot->serverConnection) return NotConnected
 
@@ -62,8 +55,7 @@ Commands::Action(Bot *bot, String channel, String message)
   if (message.length() == 0)
     return InvalidParameters;
 
-  QUEUE->sendPrivmsg(channel, String("\001ACTION ") +
-                     message + "\001");
+  Commands::CTCP (bot, channel, "ACTION", message);
 
   return Ok;
 }
@@ -83,17 +75,21 @@ Commands::AddUser(Bot *bot, String who, String maskChannel, int level,
 
   String mask;
 
-  if (!Utils::isWildcard(who)) 
+  if (!Utils::wildcard_p(who)) 
     {
-    mask = bot->getUserhost("", who);
-    if (mask.length() == 0)
-      return NotFound(who);
-  }
+      mask = bot->getUserhost("", who);
+      if (mask.length() == 0)
+       {
+         return NotFound(who);
+       }
+    }
   // Aha! This was before the brace...segfault gone
-  mask = Utils::makeWildcard(mask);
+  mask = Utils::make_wildcard(mask);
 
   if (bot->userList->isInUserList(mask, maskChannel))
-    return AlreadyInUserlist(mask, maskChannel);
+    {
+      return AlreadyInUserlist(mask, maskChannel);
+    }
 
   bot->userList->addUser(mask, maskChannel, level, prot, aop, 
                         expire, password);
@@ -128,11 +124,11 @@ Commands::AddShit(Bot *bot, String mask, String maskChannel,
 
   String who = mask;
 
-  if (!Utils::isWildcard(mask)) {
+  if (!Utils::wildcard_p(mask)) {
     mask = bot->getUserhost("", who);
     if (mask.length() == 0)
       return NotFound(who);
-    mask = Utils::makeWildcard(mask);
+    mask = Utils::make_wildcard(mask);
     if (bot->shitList->getShit(mask, maskChannel))
       return AlreadyInShitlist(mask, maskChannel);
   }
@@ -161,7 +157,7 @@ Commands::Ban(Bot *bot, String channel, String who)
 
   String dest;
 
-  if (!Utils::isWildcard(who))
+  if (!Utils::wildcard_p(who))
     dest = bot->getUserhost(channel, who);
   else
     dest = who;
@@ -169,7 +165,7 @@ Commands::Ban(Bot *bot, String channel, String who)
   if (dest.length() == 0)
     return NotFound(who);
 
-  dest = Utils::makeWildcard(dest);
+  dest = Utils::make_wildcard(dest);
   Mask m(dest);
 
   for (std::list<UserListItem *>::iterator it = bot->userList->l.begin();
@@ -179,17 +175,91 @@ 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)->banMask) && (*it)->banMask.getMask() != m.getMask())
-      QUEUE->sendChannelMode(channel, "-b", (*it)->banMask.getMask());
   
-  QUEUE->sendChannelMode(channel, "+b", dest);
+  c->delBan (m);
+  c->addBan (m, -1);
+  return Ok;
+}
+
+Message
+Commands::CTCP (Bot *bot, std::string target, std::string command,
+               std::string message)
+{
+  CHECK_CONNECTION;
+
+  if (target == "")
+    {
+      return EmptyAddressee;
+    }
+
+  if (command == "")
+    {
+      return InvalidParameters;
+    }
+
+  if (message == "")
+    {
+      return EmptyMessage;
+    }
+
+  if (Utils::channel_p (target) && !CHANNEL (target))
+    {
+      return NotOnChannel (target);
+    }
+
+
+  // Send multi-line messages as seperate privmsgs
+  StringTokenizer st_message (message);
+
+  while (st_message.more_tokens_p ('\n'))
+    {
+      QUEUE->sendCTCP (target, command, st_message.next_token ('\n'));
+    }
 
   return Ok;
 }
 
+Message
+Commands::CTCPReply (Bot *bot, std::string target, std::string command,
+                    std::string message)
+{
+  CHECK_CONNECTION;
+
+  if (target == "")
+    {
+      return EmptyAddressee;
+    }
+
+  if (command == "")
+    {
+      return InvalidParameters;
+    }
+
+  if (message == "")
+    {
+      return EmptyMessage;
+    }
+
+  // CTCP-REPLY cannot go to a channel
+  if (Utils::channel_p (target))
+    {
+      return NotToChannel;
+    }
+
+
+  // Send multi-line messages as seperate privmsgs
+  StringTokenizer st_message (message);
+
+  while (st_message.more_tokens_p ('\n'))
+    {
+      QUEUE->sendCTCPReply (target, command, st_message.next_token ('\n'));
+    }
+
+  return Ok;
+}
+
+
 Message
 Commands::Cycle(Bot *bot, String channel)
 {
@@ -219,7 +289,7 @@ Commands::Deban(Bot *bot, String channel, String who)
 
   String dest;
 
-  if (!Utils::isWildcard(who))
+  if (!Utils::wildcard_p(who))
     dest = bot->getUserhost(channel, who);
   else
     dest = who;
@@ -227,18 +297,22 @@ Commands::Deban(Bot *bot, String channel, String who)
   if (dest.length() == 0)
     return UserNotFound(who, channel);
 
-  dest = Utils::makeWildcard(dest);
+  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(), channel);
-      if (!se || !se->isStillValid() ||
-          se->getShitLevel() < ShitEntry::SHIT_NODEBAN)
-        QUEUE->sendChannelMode(channel, "-b", (*it)->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;
 }
@@ -262,11 +336,11 @@ Commands::DelUser(Bot *bot, String who, String maskChannel)
 
   String dest;
 
-  if (!Utils::isWildcard(who)) {
+  if (!Utils::wildcard_p(who)) {
     dest = bot->getUserhost("", who);
     if (dest.length() == 0)
       return NotFound(who);
-    dest = Utils::makeWildcard(who);
+    dest = Utils::make_wildcard(who);
   }
 
   if (!bot->userList->isInUserList(dest, maskChannel))
@@ -286,11 +360,11 @@ Commands::DelShit(Bot *bot, String who, String maskChannel)
 
   String dest;
 
-  if (!Utils::isWildcard(who)) {
+  if (!Utils::wildcard_p(who)) {
     dest = bot->getUserhost("", who);
     if (dest.length() == 0)
       return NotFound(who);
-    dest = Utils::makeWildcard(who);
+    dest = Utils::make_wildcard(who);
   }
 
   if (!bot->shitList->getShit(dest, maskChannel))
@@ -301,6 +375,20 @@ Commands::DelShit(Bot *bot, String who, String maskChannel)
   return Ok;
 }
 
+Commands::deop_wildcard::deop_wildcard 
+(Bot *b, Mask &m, String &c)
+  : bot (b), mask (m), channel (c)
+{ }
+
+void
+Commands::deop_wildcard::operator() (const User &user)
+{
+  if (mask.matches(user.nick + "!" + user.userhost) 
+      && user.getProt() < User::NO_DEOP 
+      && (user.mode & User::OP_MODE))
+    QUEUE->sendChannelMode(channel, "-o", user.nick);
+} 
+
 Message
 Commands::Deop(Bot *bot, String channel, String who)
 {
@@ -314,28 +402,26 @@ Commands::Deop(Bot *bot, String channel, String who)
   if (!bot->iAmOp(channel))
     return NotChannelOp(channel);
 
-  if (!Utils::isWildcard(who)) {
-    User *u = c->getUser(who);
-    if (!u)
-      return UserNotFound(who, channel);
-    if (!(u->mode & User::OP_MODE))
-      return UserNotOp(who, channel);
-    if (u->getProt() >= User::NO_DEOP)
-      return UserProtected(who, channel);
-    QUEUE->sendChannelMode(channel, "-o", who);
+  if (!Utils::wildcard_p(who)) {
+    try
+      {
+       User u = c->getUser(who);
+
+       if (!(u.mode & User::OP_MODE))
+         return UserNotOp(who, channel);
+       if (u.getProt() >= User::NO_DEOP)
+         return UserProtected(who, channel);
+       QUEUE->sendChannelMode(channel, "-o", who);
+      }
+    catch (const ChannelUserList::user_not_found &e)
+      {
+       return UserNotFound(e.name, channel);
+      }
   } else {
-    Mask m(who);
-    for (std::map<String, User *, std::less<String> >::iterator
-           it = c->channelMemory.begin();
-         it != c->channelMemory.end(); ++it) {
-      if (m.matches((*it).second->nick + "!" +
-                    (*it).second->userhost) &&
-          (*it).second->getProt() < User::NO_DEOP &&
-          ((*it).second->mode & User::OP_MODE))
-        QUEUE->sendChannelMode(channel, "-o", (*it).second->nick);
-    }
+    Mask m (who);
+    deop_wildcard f (bot, m, channel);
+    c->for_each_channel_users (f);
   }
-
   return Ok;
 }
 
@@ -377,14 +463,14 @@ Commands::Join(Bot *bot, String channel, String key)
 {
   CHECK_CONNECTION;
 
-  if (!Utils::isValidChannelName(channel))
+  if (!Utils::valid_channel_name_p(channel))
     return InvalidChannel(channel);
 
   // We change the key only if we are not on the channel.
   // We don't trust the user...
   if (!CHANNEL(channel)) {
     if (bot->wantedChannels[channel])
-      bot->wantedChannels[channel]->key = key;
+      bot->wantedChannels[channel]->key = static_cast<std::string> (key);
     else {
       bot->wantedChannels[channel] = new wantedChannel("", "", key);
     }
@@ -409,6 +495,19 @@ Commands::Keep(Bot *bot, String channel, String modes)
   return Ok;  
 }
 
+Commands::kick_wildcard::kick_wildcard 
+(Bot *b, Mask &m, String &c, String &r)
+  : bot (b), mask (m), channel (c), reason (r)
+{ }
+
+void
+Commands::kick_wildcard::operator() (const User &user)
+{
+  if (mask.matches(user.nick + "!" + user.userhost) &&
+      user.getProt() < User::NO_KICK)
+    QUEUE->sendKick(channel, user.nick, reason);
+} 
+
 Message
 Commands::Kick(Bot *bot, String channel, String who, String reason)
 {
@@ -422,24 +521,24 @@ Commands::Kick(Bot *bot, String channel, String who, String reason)
   if (!bot->iAmOp(channel))
     return NotChannelOp(channel);
 
-  if (Utils::isWildcard(who)) {
+  if (Utils::wildcard_p(who)) {
     Mask m(who);
-    for (std::map<String, User *, std::less<String> >::iterator it =
-           c->channelMemory.begin();
-         it != c->channelMemory.end();
-         ++it)
-      if (m.matches((*it).second->nick + "!" +
-                    (*it).second->userhost) &&
-          (*it).second->getProt() < User::NO_KICK)
-        QUEUE->sendKick(channel, (*it).second->nick, reason);
+    kick_wildcard f (bot, m, channel, reason);
+    c->for_each_channel_users (f);
   } else {
-    User * u = c->getUser(who);
-    if (!u)
-      return UserNotFound(who, channel);
-    if (u->getProt() < User::NO_KICK)
-      QUEUE->sendKick(channel, who, reason);
-    else
-      return UserProtected(who, channel);
+    try
+      {
+       User u = c->getUser(who);
+
+       if (u.getProt() < User::NO_KICK)
+         QUEUE->sendKick(channel, who, reason);
+       else
+         return UserProtected(who, channel);
+      }
+    catch (const ChannelUserList::user_not_found &e)
+      {
+       return UserNotFound(e.name, channel);
+      }
   }
 
   return Ok;
@@ -495,15 +594,30 @@ Commands::Msg(Bot *bot, String who, String message)
   CHECK_CONNECTION;
 
   if (who == "")
-    return EmptyAddressee;
+    {
+      return EmptyAddressee;
+    }
 
-  if (Utils::isChannel(who))
-    return NotToChannel;
+  if (Utils::channel_p(who))
+    {
+      if (!CHANNEL(who))
+       {
+         return NotOnChannel (who);
+       }
+    }
 
   if (message == "")
-    return EmptyMessage;
+    {
+      return EmptyMessage;
+    }
 
-  QUEUE->sendPrivmsg(who, message);
+  // Send multi-line messages as seperate privmsgs
+  StringTokenizer st_message (message);
+
+  while (st_message.more_tokens_p ('\n'))
+    {
+      QUEUE->sendPrivmsg(who, st_message.next_token ('\n'));
+    }
 
   return Ok;
 }
@@ -518,6 +632,16 @@ Commands::NextServer(Bot *bot)
 
   if (!bot->canChangeServer())
     return CanNotChangeServer;
+
+#ifdef USESCRIPTS
+  // Run hooks/disconnect
+  bot->botInterp->RunHooks 
+    (Hook::DISCONNECT, 
+     bot->serverConnection->server->getHostName (),
+     scm_list_n 
+     (Utils::str2scm  (bot->serverConnection->server->getHostName ()),
+      SCM_BOOL_T));
+#endif
   
   QUEUE->sendQuit("Changing server");
   bot->nextServer();
@@ -530,7 +654,7 @@ Commands::Nick(Bot *bot, String nick)
 {
   CHECK_CONNECTION;
 
-  if (nick == "" || !Utils::isValidNickName(nick))
+  if (nick == "" || !Utils::valid_nickname_p(bot, nick))
     return InvalidNick(nick);
   
   bot->wantedNickName = nick;
@@ -547,13 +671,19 @@ Commands::Notice(Bot *bot, String who, String message)
   if (who == "")
     return EmptyAddressee;
 
-  if (Utils::isChannel(who))
-    return NotToChannel;
+  //  if (Utils::channel_p(who))
+  //    return NotToChannel;
 
   if (message == "")
     return EmptyMessage;
 
-  QUEUE->sendNotice(who, message);
+  // Send multiple lines as multiple notices
+  StringTokenizer st_message (message);
+
+  while (st_message.more_tokens_p ('\n'))
+    {
+      QUEUE->sendNotice(who, st_message.next_token ('\n'));
+    }
 
   return Ok;
 }
@@ -571,18 +701,23 @@ Commands::Op(Bot *bot, String channel, String who)
   if (!bot->iAmOp(channel))
     return NotChannelOp(channel);
 
-  if (Utils::isWildcard(who))
+  if (Utils::wildcard_p(who))
     return MassOpNotAllowed;
+  
+  try
+    {
+      User u = c->getUser(who);
 
-  User *u = c->getUser(who);
-  if (!u)
-    return UserNotFound(who, channel);
-
-  ShitEntry *se = bot->shitList->getShit(who, channel);
-  if (se && se->isStillValid() && se->getShitLevel() >= ShitEntry::SHIT_NOOP)
-    return UserOnShitList(who);
-
-  QUEUE->sendChannelMode(channel, "+o", who);
+      ShitEntry *se = bot->shitList->getShit(who, channel);
+      if (se && se->isStillValid() && se->getShitLevel() >= ShitEntry::SHIT_NOOP)
+       return UserOnShitList(who);
+      
+      QUEUE->sendChannelMode(channel, "+o", who);
+    }
+  catch (const ChannelUserList::user_not_found &e)
+    {
+      return UserNotFound(e.name, channel);
+    }
 
   return Ok;
 }
@@ -621,14 +756,7 @@ Commands::Reconnect(Bot *bot)
 Message
 Commands::Say(Bot *bot, String channel, String message)
 {
-  CHECK_CONNECTION;
-
-//   if (!CHANNEL(channel))
-//     return NotOnChannel(channel);
-
-  QUEUE->sendPrivmsg(channel, message);
-
-  return Ok;
+  return Commands::Msg (bot, channel, message);
 }
 
 
@@ -650,6 +778,17 @@ Commands::Server(Bot *bot, int number)
   return Ok;
 }
 
+Message
+Commands::SetFloodRate(Bot *bot, unsigned int num_messages)
+{
+  if (num_messages > 0)
+    {
+      bot->MAX_MESSAGES = num_messages;
+      return Ok;
+    }
+  return InvalidParameters;
+}
+
 Message
 Commands::SetVersion(Bot *bot, String str)
 {
@@ -666,45 +805,46 @@ Commands::TBan(Bot *bot, String channel, String who, int seconds)
   CHECK_CONNECTION;
 
   Channel *c = CHANNEL(channel);
+  String dest;
 
+  // Make sure all of the inputs are valid
   if (!c)
     return NotOnChannel(channel);
-
   if (!bot->iAmOp(channel))
     return NotChannelOp(channel);
-
   if (seconds <= 0)
     return InvalidTime(seconds);
-
-  String dest;
-
-  if (!Utils::isWildcard(who))
+  
+  // Look for user
+  if (!Utils::wildcard_p(who))
     dest = bot->getUserhost(channel, who);
   else
     dest = who;
-
+  
   if (dest.length() == 0)
     return UserNotFound(who, channel);
 
-  dest = Utils::makeWildcard(dest);
+  dest = Utils::make_wildcard(dest);
   Mask m(dest);
 
+  // Make sure the user isn't protected from bans
   for (std::list<UserListItem *>::iterator it = bot->userList->l.begin();
        it != bot->userList->l.end();
        it++)
-    if (m.matches((*it)->mask) &&
-        (*it)->channelMask.matches(channel) &&
-        (*it)->prot >= User::NO_BAN)
-      return UserProtected(who, channel);
+    {
+      if (m.matches((*it)->mask) &&
+         (*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)->banMask))
-      QUEUE->sendChannelMode(channel, "-b", (*it)->banMask.getMask());
+  //  c->delBan (dest);
+  c->addBan(dest, seconds);
 
-  CHANNEL(channel)->addBan(dest, seconds);
-  QUEUE->sendChannelMode(channel, "+b", dest);
-  bot->todoList->addDeban(channel, dest, seconds);
+  return Ok;
 }
 
 
@@ -758,3 +898,31 @@ Commands::Unlock(Bot *bot, String channel)
 
   return Ok;
 }
+
+Message
+Commands::Who (Bot *bot, String target)
+{
+  CHECK_CONNECTION;
+
+  QUEUE->sendWho (target);
+
+  return Ok;
+}
+
+Message
+Commands::Whois (Bot *bot, String nick)
+{
+  CHECK_CONNECTION;
+
+  if (!Utils::valid_nickname_p (bot, nick))
+    {
+      return InvalidNick (nick);
+    }
+  else
+    {
+      QUEUE->sendWhois (nick);
+      return Ok;
+    }
+}
+
+