// Commands.C -*- C++ -*- // Copyright (c) 1998 Etienne BERNARD // 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 // 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. #include "Macros.H" #include "Message.H" #include "Commands.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.")) #define CHECK_CONNECTION if (!bot->serverConnection) return NotConnected Message Commands::Action(Bot *bot, String channel, String message) { CHECK_CONNECTION; if (!CHANNEL(channel)) return NotOnChannel(channel); if (message.length() == 0) return InvalidParameters; QUEUE->sendCTCP (channel, "ACTION", message); return Ok; } Message Commands::AddUser(Bot *bot, String who, String maskChannel, int level, int prot, bool aop, std::time_t expire, String password) { // Gah, fix this (makes bot segfault) if (who.length() == 0 || maskChannel.length() == 0 || level < 0 || level > User::FRIEND || prot < 0 || prot > User::NO_DEOP) return InvalidParameters; String mask; if (!Utils::wildcard_p(who)) { mask = bot->getUserhost("", who); if (mask.length() == 0) { return NotFound(who); } } // Aha! This was before the brace...segfault gone mask = Utils::make_wildcard(mask); if (bot->userList->isInUserList(mask, maskChannel)) { return AlreadyInUserlist(mask, maskChannel); } bot->userList->addUser(mask, maskChannel, level, prot, aop, expire, password); bot->rehash(); return Ok; } Message Commands::AddServer(Bot *bot, String servername, int port) { if (port <= 0) return InvalidPort(port); bot->serverList->addServer(new class Server(servername, port)); return Ok; } Message Commands::AddShit(Bot *bot, String mask, String maskChannel, int level, time_t expiration, String reason) { if (mask.length() == 0 || maskChannel.length() == 0 || level < 0 || level > ShitEntry::SHIT_NODEBAN) return InvalidParameters; if (reason == "") reason = "You're on my shitlist, lamer"; String who = mask; if (!Utils::wildcard_p(mask)) { mask = bot->getUserhost("", who); if (mask.length() == 0) return NotFound(who); mask = Utils::make_wildcard(mask); if (bot->shitList->getShit(mask, maskChannel)) return AlreadyInShitlist(mask, maskChannel); } if (bot->userList->getMaxProt(mask, maskChannel) > 0) return UserProtected(who, maskChannel); bot->shitList->addShit(mask, maskChannel, level, expiration, reason); return Ok; } Message Commands::Ban(Bot *bot, String channel, String who) { CHECK_CONNECTION; Channel *c = CHANNEL(channel); if (!c) return NotOnChannel(channel); if (!bot->iAmOp(channel)) return NotChannelOp(channel); String dest; if (!Utils::wildcard_p(who)) dest = bot->getUserhost(channel, who); else dest = who; if (dest.length() == 0) return NotFound(who); dest = Utils::make_wildcard(dest); Mask m(dest); for (std::list::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); for (std::vector::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); return Ok; } Message Commands::Cycle(Bot *bot, String channel) { CHECK_CONNECTION; if (!CHANNEL(channel)) return NotOnChannel(channel); QUEUE->sendPart(channel); QUEUE->sendJoin(channel, bot->wantedChannels[channel]->key); return Ok; } Message Commands::Deban(Bot *bot, String channel, String who) { CHECK_CONNECTION; Channel *c = CHANNEL(channel); if (!c) return NotOnChannel(channel); if (!bot->iAmOp(channel)) return NotChannelOp(channel); String dest; if (!Utils::wildcard_p(who)) dest = bot->getUserhost(channel, who); else dest = who; if (dest.length() == 0) return UserNotFound(who, channel); dest = Utils::make_wildcard(dest); Mask m(dest); 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(), channel); if (!se || !se->isStillValid() || se->getShitLevel() < ShitEntry::SHIT_NODEBAN) QUEUE->sendChannelMode(channel, "-b", (*it)->getMask()); } return Ok; } Message Commands::DelServer(Bot *bot, int number) { if (number < 0 || number >= bot->serverList->size()) return InvalidServerNumber(number); bot->serverList->delServer(number); return Ok; } Message Commands::DelUser(Bot *bot, String who, String maskChannel) { if (who.length() == 0 || maskChannel.length() == 0) return InvalidParameters; String dest; if (!Utils::wildcard_p(who)) { dest = bot->getUserhost("", who); if (dest.length() == 0) return NotFound(who); dest = Utils::make_wildcard(who); } if (!bot->userList->isInUserList(dest, maskChannel)) return NotInUserlist(who); bot->userList->removeUser(dest, maskChannel); bot->rehash(); return Ok; } Message Commands::DelShit(Bot *bot, String who, String maskChannel) { if (who.length() == 0 || maskChannel.length() == 0) return InvalidParameters; String dest; if (!Utils::wildcard_p(who)) { dest = bot->getUserhost("", who); if (dest.length() == 0) return NotFound(who); dest = Utils::make_wildcard(who); } if (!bot->shitList->getShit(dest, maskChannel)) return NotInShitlist(who); bot->shitList->delShit(dest, maskChannel); return Ok; } Message Commands::Deop(Bot *bot, String channel, String who) { CHECK_CONNECTION; Channel *c = CHANNEL(channel); if (!c) return NotOnChannel(channel); if (!bot->iAmOp(channel)) return NotChannelOp(channel); if (!Utils::wildcard_p(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); } else { Mask m(who); for (std::map >::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); } } return Ok; } Message Commands::Die(Bot *bot, String reason) { CHECK_CONNECTION; QUEUE->sendQuit(reason); bot->stop = true; return Ok; } Message Commands::Do(Bot *bot, String command) { CHECK_CONNECTION; QUEUE->addLine(command, 0, 0, ServerQueueItem::OTHER); return Ok; } Message Commands::Invite(Bot *bot, String channel, String who) { CHECK_CONNECTION; if (!bot->iAmOp(channel)) return NotChannelOp(channel); QUEUE->sendInvite(channel, who); return Ok; } Message Commands::Join(Bot *bot, String channel, String key) { CHECK_CONNECTION; 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; else { bot->wantedChannels[channel] = new wantedChannel("", "", key); } } QUEUE->sendJoin(channel, bot->wantedChannels[channel]->key); return Ok; } Message Commands::Keep(Bot *bot, String channel, String modes) { CHECK_CONNECTION; Channel *c = CHANNEL(channel); if (!c) return NotOnChannel(channel); c->keepModes = modes; return Ok; } Message Commands::Kick(Bot *bot, String channel, String who, String reason) { CHECK_CONNECTION; Channel *c = CHANNEL(channel); if (!c) return NotOnChannel(channel); if (!bot->iAmOp(channel)) return NotChannelOp(channel); if (Utils::wildcard_p(who)) { Mask m(who); for (std::map >::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); } 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); } return Ok; } Message Commands::KickBan(Bot *bot, String channel, String who, String reason) { CHECK_CONNECTION; Message m = Commands::Ban(bot, channel, who); if (m.getCode() == 0) m = Commands::Kick(bot, channel, who, reason); return m; } Message Commands::Lock(Bot *bot, String channel) { CHECK_CONNECTION; Channel *c = CHANNEL(channel); if (!c) return NotOnChannel(channel); c->lockedTopic = true; return Ok; } Message Commands::Mode(Bot *bot, String channel, String mode) { CHECK_CONNECTION; if (!CHANNEL(channel)) return NotOnChannel(channel); if (!bot->iAmOp(channel)) return NotChannelOp(channel); QUEUE->sendChannelMode(String("MODE ") + channel + " " + mode); return Ok; } Message Commands::Msg(Bot *bot, String who, String message) { CHECK_CONNECTION; if (who == "") return EmptyAddressee; if (Utils::channel_p(who)) return NotToChannel; if (message == "") return EmptyMessage; QUEUE->sendPrivmsg(who, message); return Ok; } Message Commands::NextServer(Bot *bot) { CHECK_CONNECTION; if (bot->serverList->size() == 0) return EmptyServerList; if (!bot->canChangeServer()) return CanNotChangeServer; QUEUE->sendQuit("Changing server"); bot->nextServer(); return Ok; } Message Commands::Nick(Bot *bot, String nick) { CHECK_CONNECTION; if (nick == "" || !Utils::valid_nickname_p(nick)) return InvalidNick(nick); bot->wantedNickName = nick; QUEUE->sendNick(nick); return Ok; } Message Commands::Notice(Bot *bot, String who, String message) { CHECK_CONNECTION; if (who == "") return EmptyAddressee; if (Utils::channel_p(who)) return NotToChannel; if (message == "") return EmptyMessage; QUEUE->sendNotice(who, message); return Ok; } Message Commands::Op(Bot *bot, String channel, String who) { CHECK_CONNECTION; Channel *c = CHANNEL(channel); if (!c) return NotOnChannel(channel); if (!bot->iAmOp(channel)) return NotChannelOp(channel); if (Utils::wildcard_p(who)) return MassOpNotAllowed; 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); return Ok; } Message Commands::Part(Bot *bot, String channel) { CHECK_CONNECTION; if (!CHANNEL(channel)) return NotOnChannel(channel); wantedChannel *w = bot->wantedChannels[channel]; bot->wantedChannels.erase(channel); delete w; QUEUE->sendPart(channel); return Ok; } Message Commands::Reconnect(Bot *bot) { CHECK_CONNECTION; if (!bot->canChangeServer()) return CanNotChangeServer; QUEUE->sendQuit("Reconnecting"); bot->reconnect(); return Ok; } Message Commands::Say(Bot *bot, String channel, String message) { CHECK_CONNECTION; // if (!CHANNEL(channel)) // return NotOnChannel(channel); QUEUE->sendPrivmsg(channel, message); return Ok; } Message Commands::Server(Bot *bot, int number) { CHECK_CONNECTION; if (number < 0 || number >= bot->serverList->size()) return InvalidServerNumber(number); if (!bot->canChangeServer()) return CanNotChangeServer; QUEUE->sendQuit("Changing server"); QUEUE->flush(); bot->connect(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) { if (str.length() == 0) return InvalidParameters; bot->versionString = str; return Ok; } Message 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); } // 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::make_wildcard(dest); Mask m(dest); // Make sure the user isn't protected from bans for (std::list::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); } } // Clear existing bans on the user for (std::vector::iterator it = c->channelBanlist.begin(); it != c->channelBanlist.end(); ++it) { if (m.matches((*it)->banMask)) { QUEUE->sendChannelMode(channel, "-b", (*it)->banMask.getMask()); } } // Ban them CHANNEL(channel)->addBan(dest, seconds); QUEUE->sendChannelMode(channel, "+b", dest); bot->todoList->addDeban(channel, dest, seconds); return Ok; } Message Commands::TKBan(Bot *bot, String channel, String who, int seconds, String reason) { CHECK_CONNECTION; Message m = Commands::TBan(bot, channel, who, seconds); if (m.getCode() == 0) m = Commands::Kick(bot, channel, who, reason); return m; } Message Commands::Topic(Bot *bot, String channel, String topic) { CHECK_CONNECTION; Channel *c = CHANNEL(channel); if (!c) return NotOnChannel(channel); if (!bot->iAmOp(channel) && !(c->channelMode & Channel::TOPIC_RESTRICTED)) return CanNotChangeTopic(channel); if (c->lockedTopic) return TopicLocked(channel); QUEUE->sendTopic(channel, topic); return Ok; } Message Commands::Unlock(Bot *bot, String channel) { CHECK_CONNECTION; Channel *c = CHANNEL(channel); if (!c) return NotOnChannel(channel); c->lockedTopic = false; return Ok; }