X-Git-Url: https://git.hcoop.net/clinton/bobotpp.git/blobdiff_plain/cb21075d829be5ad97d026daf76061c13d4bc171..c7d8cfb9a5db8174d122e7140fbdce5711f48778:/source/Parser.C diff --git a/source/Parser.C b/source/Parser.C dissimilarity index 91% index 42417e2..cbecfe7 100644 --- a/source/Parser.C +++ b/source/Parser.C @@ -1,998 +1,1051 @@ -// Parser.C -*- C++ -*- -// Copyright (c) 1997, 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 -// 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. - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include - -#include "StringTokenizer.H" -#include "Parser.H" -#include "UserCommands.H" -#include "Macros.H" -#include "Utils.H" -#include "ShitList.H" - -struct { - char *name; - void (*function)(ServerConnection *, Person *, String); -} functions[] = -{ - { "001", Parser::parse001 }, /* RPL_WELCOME */ - { "302", Parser::parse302 }, /* RPL_USERHOST */ - { "311", Parser::parse311 }, /* RPL_WHOISUSER */ - { "315", Parser::parse315 }, /* RPL_ENDOFWHO */ - { "324", Parser::parse324 }, /* RPL_CHANNELMODEIS */ - { "332", Parser::parse332 }, /* RPL_TOPIC */ - { "352", Parser::parse352 }, /* RPL_WHOREPLY */ - { "353", Parser::parse353 }, /* RPL_NAMESREPLY */ - { "366", Parser::parse366 }, /* RPL_ENDOFNAMES */ - { "367", Parser::parse367 }, /* RPL_BANLIST */ - { "401", Parser::parse401 }, /* ERR_NOSUCHNICK */ - { "433", Parser::parse433 }, /* ERR_NICKNAMEINUSE */ - { "437", Parser::parse433 }, /* ERR_UNAVAILRESOURCE */ - { "471", Parser::parse473 }, /* ERR_CHANNELISFULL */ - { "473", Parser::parse473 }, /* ERR_INVITEONLYCHAN */ - { "474", Parser::parse473 }, /* ERR_BANNEDFROMCHAN */ - { "475", Parser::parse473 }, /* ERR_BADCHANNELKEY */ - { "ERROR", Parser::parseError }, - { "INVITE", Parser::parseInvite }, - { "JOIN", Parser::parseJoin }, - { "KICK", Parser::parseKick }, - { "MODE", Parser::parseMode }, - { "NICK", Parser::parseNick }, - { "NOTICE", Parser::parseNotice }, - { "PART", Parser::parsePart }, - { "PING", Parser::parsePing }, - { "PONG", Parser::parsePong }, - { "PRIVMSG", Parser::parsePrivmsg }, - { "QUIT", Parser::parseQuit }, - { "TOPIC", Parser::parseTopic }, - { "", Parser::parseError }, - { 0, 0 } -}; - - -void -Parser::parseLine(ServerConnection * cnx, String line) -{ - StringTokenizer st(line); - Person * from = 0; - -#ifdef USESCRIPTS - cnx->bot->botInterp->RunHooks(Hook::RAW, line, - scm_listify (Utils::string2SCM(line), - SCM_UNDEFINED)); -#endif - - if (line[0] == ':') { - String fromMask = st.nextToken().subString(1); - if (fromMask.find('!') != -1) - from = new Person(cnx->bot, fromMask); - } - - String command = st.nextToken(); - String rest = st.rest(); - - for (int i = 0; functions[i].name != 0; i++) - if (command == functions[i].name) { - functions[i].function(cnx, from, rest); - break; - } - - delete from; -} - -void -Parser::parse001(ServerConnection * cnx, - Person *from, String rest) -{ - String temp = ""; - StringTokenizer st(rest); - String realNick = st.nextToken(); - - if ((cnx->bot->nickName).toLower() != realNick) { - // Yes, this can happen, and it was a very subtle bug - cnx->bot->nickName = realNick; - cnx->bot->userList->removeFirst(); - cnx->bot->userList->addUserFirst(realNick + "!" + cnx->bot->userHost, "*", 0, 3, true, -1, ""); - cnx->bot->lastNickNameChange = time(0); - cnx->bot->rehash(); - } - - cnx->bot->connected = true; - - cnx->queue->sendUserMode(cnx->bot->nickName, "+i"); - cnx->queue->sendWhois(cnx->bot->nickName); - - for (std::map >::iterator - it = cnx->bot->wantedChannels.begin(); - it != cnx->bot->wantedChannels.end(); ++it) - cnx->queue->sendJoin((*it).first, (*it).second->key); - - cnx->bot->logLine(String("Connected to server ") + - cnx->bot->serverList->currentServer()->getHostName() + - " (" + String((long)cnx->bot->serverList->currentServer()->getPort()) + - ")."); -} - -void -Parser::parse302(ServerConnection *cnx, - Person *from, String rest) -{ - unsigned long num = cnx->bot->receivedUserhostID++; - StringTokenizer st(rest); - - st.nextToken(':'); - - if (st.rest().length()) { - st.nextToken('='); - String parameters = st.rest(); - parameters = parameters.subString(1); - cnx->bot->userhostMap[num] = parameters; - } else - cnx->bot->userhostMap[num] = ""; -} - -void -Parser::parse311(ServerConnection *cnx, - Person *from, String rest) -{ - StringTokenizer st(rest); - st.nextToken(); - String nuh = st.nextToken() + "!"; - String uh = st.nextToken() + "@"; - uh = uh + st.nextToken(); - nuh = nuh + uh; - cnx->bot->userList->addUserFirst(nuh, "*", 0, 3, true, -1, ""); - cnx->bot->userHost = uh; -} - -void -Parser::parse315(ServerConnection *cnx, - Person *from, String rest) -{ - StringTokenizer st(rest); - st.nextToken(); - String channel = st.nextToken(); - Channel *c = cnx->bot->channelList->getChannel(channel); - if (!c) - return; - c->gotWho = true; -} - -void -Parser::parse324(ServerConnection *cnx, - Person *from, String rest) -{ - StringTokenizer st(rest); - st.nextToken(); - String channel = st.nextToken(); - if (Channel *c = cnx->bot->channelList->getChannel(channel)) - if (c) c->parseMode(from, st.rest()); -} - -void -Parser::parse332(ServerConnection *cnx, - Person *from, String rest) -{ - StringTokenizer st(rest); - st.nextToken(); - String channel = st.nextToken(); - if (Channel *c = cnx->bot->channelList->getChannel(channel)) - if (c) c->channelTopic = st.rest().subString(1); -} - -void -Parser::parse352(ServerConnection *cnx, - Person *from, String rest) -{ - StringTokenizer st(rest); - st.nextToken(); - String ch = st.nextToken(); - String uh = st.nextToken() + "@"; - uh = uh + st.nextToken(); - st.nextToken(); - String n = st.nextToken(); - String m = st.nextToken(); - int mode = 0; - - for (int i = 0; i < m.length(); i++) - switch (m[i]) { - case 'H': break; - case 'G': mode |= User::AWAY_MODE; break; - case '*': mode |= User::IRCOP_MODE; break; - case '@': mode |= User::OP_MODE; break; - case '+': mode |= User::VOICE_MODE; break; - } - if (Channel *c = cnx->bot->channelList->getChannel(ch)) - if (c) c->addNick(n, uh, mode, cnx->bot->userList); -} - -void -Parser::parse353(ServerConnection *cnx, - Person *from, String rest) -{ - int mode = 0; - String nick; - - StringTokenizer st(rest); - st.nextToken(); st.nextToken(); - - Channel * c = cnx->bot->channelList->getChannel(st.nextToken()); - if (!c) return; - StringTokenizer st2(st.nextToken(':')); - - while (st2.hasMoreTokens()) { - nick = st2.nextToken(); - if (nick[0] == '@') { - mode = User::OP_MODE; - nick = nick.subString(1); - } else if (nick[0] == '+') { - mode = User::VOICE_MODE; - nick = nick.subString(1); - } - c->addNick(nick, "", mode, 0, true); - } -} - -void -Parser::parse366(ServerConnection *cnx, - Person *from, String rest) -{ - StringTokenizer st(rest); - st.nextToken(); - String ch = st.nextToken(); - if (Channel *c = cnx->bot->channelList->getChannel(ch)) - c->joined = true; -} - -void -Parser::parse367(ServerConnection *cnx, - Person *from, String rest) -{ - StringTokenizer st(rest); - st.nextToken(); - String ch = st.nextToken(); - if (Channel *c = cnx->bot->channelList->getChannel(ch)) - c->addBan(st.nextToken(), -1); -} - -void -Parser::parse401(ServerConnection *cnx, - Person *from, String rest) -{ - StringTokenizer st(rest); - st.nextToken(); - String nick = st.nextToken(); - - if (cnx->bot->spyList.find(nick) != cnx->bot->spyList.end()) { - delete cnx->bot->spyList[nick]; - cnx->bot->spyList.erase(nick); - } -} - -void -Parser::parse433(ServerConnection *cnx, - Person *from, String rest) -{ - if (cnx->bot->connected) - return; - - if (cnx->bot->nickName.length() == 9) { - int i; - for (i = 0; i < cnx->bot->nickName.length() && cnx->bot->nickName[i] == '_'; i++) - ; - if (i < cnx->bot->nickName.length()) - cnx->bot->nickName = cnx->bot->nickName.subString(0, i-1) + "_" + cnx->bot->nickName.subString(i+1); - else - cnx->bot->nickName = cnx->bot->nickName.subString(0, 4) + - String((long)(rand() % 10000)); - } - else - cnx->bot->nickName = cnx->bot->nickName + "_"; - - cnx->queue->sendNick(cnx->bot->nickName); -} - -void -Parser::parse473(ServerConnection *cnx, - Person *from, String rest) -{ - StringTokenizer st(rest); - st.nextToken(); - - cnx->bot->logLine(String("Unable to join channel ") + - st.nextToken() + "."); -} - -void -Parser::parseError(ServerConnection *cnx, - Person *from, String rest) -{ - cnx->bot->logLine(String("Error from server ") + - cnx->bot->serverList->currentServer()->getHostName() + - " (" + String((long)cnx->bot->serverList->currentServer()->getPort()) + - ")."); - cnx->bot->nextServer(); -} - -void -Parser::parseInvite(ServerConnection *cnx, - Person *from, String rest) -{ - String nick = from->getNick(); - StringTokenizer st(rest); - st.nextToken(':'); - String channel = st.rest(); - -#ifdef USESCRIPTS - cnx->bot->botInterp->RunHooks(Hook::INVITE, nick + " " + channel, - scm_listify (Utils::string2SCM(nick), - Utils::string2SCM(channel), - SCM_UNDEFINED)); -#endif - - if (cnx->bot->wantedChannels.find(channel) != - cnx->bot->wantedChannels.end()) - cnx->queue->sendJoin(channel, cnx->bot->wantedChannels[channel]->key); -} - -void -Parser::parseJoin(ServerConnection *cnx, - Person *from, String rest) -{ - StringTokenizer st(from->getAddress()); - String n = st.nextToken('!'); - String uh = st.nextToken(); - StringTokenizer st2(rest); - String c = st2.nextToken(':'); - String mode; - bool joinAndMode = false; - -#ifdef USESCRIPTS - cnx->bot->botInterp->RunHooks(Hook::JOIN, n + " " + c, - scm_listify (Utils::string2SCM(n), - Utils::string2SCM(c), - SCM_UNDEFINED)); -#endif - - // This part of code is for the combined JOIN & MODE of ircd 2.9 - if (c.find('\007') >= 0) { - joinAndMode = true; - StringTokenizer st3(c); - c = st3.nextToken('\007'); - String m = st3.rest(); - mode = c + " +" + m; - for (int i = 0; i < m.length(); i++) - mode = mode + " " + n; - } - - if (n == cnx->bot->nickName) { - cnx->bot->logLine(String("Joined channel ") + c + "."); - if (cnx->bot->wantedChannels.find(c) != cnx->bot->wantedChannels.end()) - cnx->bot->channelList->addChannel(cnx, c, cnx->bot->wantedChannels[c]->keep); - else - cnx->bot->channelList->addChannel(cnx, c); - cnx->queue->sendWho(c); - cnx->queue->sendChannelMode(String("MODE ") + c + " b"); - cnx->queue->sendChannelMode(String("MODE ") + c); - } else { - Channel * ch = cnx->bot->channelList->getChannel(c); - if (!ch) - return; - ShitEntry * se = cnx->bot->shitList->getShit(n+"!"+uh, c); - if (se && se->isStillValid() && - se->getShitLevel() >= ShitEntry::SHIT_NOJOIN) { - cnx->queue->sendChannelMode(c, "+b", se->getMask()); - cnx->queue->sendKick(c, n, se->getShitReason()); - return; - } - ch->addNick(n, uh, 0, cnx->bot->userList); - if (ch->getUser(n)->getAop() && !(ch->getUser(n)->mode & User::OP_MODE) && cnx->bot->iAmOp(c)) { - // This is a part of the antispoof code - ch->getUser(n)->userkey = Utils::getKey(); - cnx->queue->sendCTCP(n, "PING", ch->getUser(n)->userkey + " " + c); - } - } - - if (joinAndMode) - parseMode(cnx, 0, mode); -} - -void -Parser::parseKick(ServerConnection *cnx, - Person *from, String rest) -{ - StringTokenizer st(rest); - String channel = st.nextToken(); - String target = st.nextToken(); - String reason = st.rest().subString(1); - -#ifdef USESCRIPTS - cnx->bot->botInterp->RunHooks(Hook::KICK, target + " " + from->getNick() + " " - + channel + " " + reason, - scm_listify (Utils::string2SCM(target), - Utils::string2SCM(from->getNick()), - Utils::string2SCM(channel), - Utils::string2SCM(reason), - SCM_UNDEFINED)); -#endif - - if (target == cnx->bot->nickName) { - cnx->bot->logLine(from->getAddress() + " kicked me out of channel " + - channel + " (" + reason + ")."); - cnx->queue->sendJoin(channel, cnx->bot->channelList->getChannel(channel)->channelKey); - cnx->bot->channelList->delChannel(channel); - } else { - if (!cnx->bot->channelList->getChannel(channel)) return; - User *u = cnx->bot->channelList->getChannel(channel)->getUser(target); - if (u && u->getProt() >= User::NO_KICK) { - String fromNick = from->getNick(); - User *v = cnx->bot->channelList->getChannel(channel)->getUser(fromNick); - if (v->getProt() < User::NO_KICK) { - cnx->bot->logLine(from->getAddress() + " kicked " + target + - " (protected) out of channel " + channel + - " (" + reason + ")."); - cnx->queue->sendKick(channel, fromNick, - target + " \002is protected !\002"); - } - } - cnx->bot->channelList->getChannel(channel)->delNick(target); - } -} - -void -Parser::parseMode(ServerConnection *cnx, - Person *from, String rest) -{ - StringTokenizer st(rest); - String ch = st.nextToken(); - String modes = st.rest(); - -#ifdef USESCRIPTS - if (from) - cnx->bot->botInterp->RunHooks(Hook::MODE, from->getNick() + " " + ch + - " " + modes, - scm_listify (Utils::string2SCM(from->getNick()), - Utils::string2SCM(ch), - Utils::string2SCM(modes), - SCM_UNDEFINED)); -#endif - - - if (Utils::isChannel(ch)) { - Channel *c = cnx->bot->channelList->getChannel(ch); - if (!c) - return; - if (from) - c->parseMode(from, modes); - else - c->parseMode(0, modes); - } -} - -void -Parser::parseNick(ServerConnection *cnx, - Person *from, String rest) -{ - String on_orig = from->getNick(); - String on = on_orig.toLower(); - String nn = rest.subString(1); - String nn_lower = nn.toLower(); - -#ifdef USESCRIPTS - cnx->bot->botInterp->RunHooks(Hook::NICKNAME, on_orig + " " + nn, - scm_listify (Utils::string2SCM(on_orig), - Utils::string2SCM(nn), - SCM_UNDEFINED)); -#endif - - if ((cnx->bot->nickName).toLower() == on) { - cnx->bot->userList->removeFirst(); - cnx->bot->userList->addUserFirst(nn + "!" + cnx->bot->userHost, "*", 0, 3, true, -1, ""); - cnx->bot->lastNickNameChange = time(0); - cnx->bot->nickName = nn; - cnx->bot->rehash(); - } - - if (cnx->bot->spyList.find(on) != cnx->bot->spyList.end()) { - cnx->bot->spyList[nn_lower] = cnx->bot->spyList[on]; - cnx->bot->spyList.erase(on); - } - - for (std::map >::iterator it = - cnx->bot->channelList->begin(); - it != cnx->bot->channelList->end(); - ++it) - if ((*it).second->hasNick(on)) - (*it).second->changeNick(on, nn_lower); -} - -void -Parser::parseNotice(ServerConnection *cnx, - Person *from, String rest) -{ - String nick = ""; - - if (from) - nick = from->getNick(); - - StringTokenizer st(rest); - String to = st.nextToken(); - - rest = st.rest().subString(1); - - if (rest[0] != '\001') { -#ifdef USESCRIPTS - if (Utils::isChannel(to)) - cnx->bot->botInterp->RunHooks(Hook::PUBLIC_NOTICE, nick + " " + - to + " " + rest, - scm_listify (Utils::string2SCM(nick), - Utils::string2SCM(to), - Utils::string2SCM(rest), - SCM_UNDEFINED)); - else - cnx->bot->botInterp->RunHooks(Hook::NOTICE, nick + " " + rest, - scm_listify (Utils::string2SCM(nick), - Utils::string2SCM(rest), - SCM_UNDEFINED)); -#endif - return; - } - - rest = rest.subString(1, rest.length() - 2); - StringTokenizer st2(rest); - String command = st2.nextToken(); - rest = st2.rest(); - -#ifdef USESCRIPTS - cnx->bot->botInterp->RunHooks(Hook::CTCP_REPLY, nick + " " + command + - " " + rest, - scm_listify (Utils::string2SCM(nick), - Utils::string2SCM(command), - Utils::string2SCM(rest), - SCM_UNDEFINED)); -#endif - - if (command == "PING") { - StringTokenizer st3(rest); - rest = st3.nextToken(); - String c = st3.rest(); - if (cnx->bot->channelList->getChannel(c) && - cnx->bot->channelList->getChannel(c)->getUser(nick) && - cnx->bot->channelList->getChannel(c)->getUser(nick)->getAop() && - !(cnx->bot->channelList->getChannel(c)->getUser(nick)->mode & User::OP_MODE) - && cnx->bot->channelList->getChannel(c)->getUser(nick)->userkey == rest) - cnx->queue->sendChannelMode(c, "+o", nick); - } -} - -void -Parser::parsePrivmsg(ServerConnection *cnx, - Person *from, String rest) -{ - String nick = from->getNick(); - - StringTokenizer st(rest); - String to = st.nextToken(); - String fromUserhost = Utils::getUserhost(from->getAddress()); - - rest = st.rest().subString(1); - - if (++(cnx->bot->ignoredUserhosts[fromUserhost]) - > Bot::MAX_MESSAGES) { - if (cnx->bot->ignoredUserhosts[fromUserhost] - == Bot::MAX_MESSAGES+1) { -#ifdef USESCRIPTS - cnx->bot->botInterp->RunHooks(Hook::FLOOD, nick, - scm_listify (Utils::string2SCM(nick), - SCM_UNDEFINED)); -#endif - cnx->bot->ignoredUserhosts[fromUserhost] += Bot::IGNORE_DELAY; - cnx->bot->logLine(from->getAddress() + - " is flooding me. We will ignore him/her/it."); - if (!Utils::isChannel(to)) - from->sendNotice(String("\002You are now being ignored for ") + - String((long)Bot::IGNORE_DELAY) + " seconds.\002"); - } - // The following lines reset the counter if you use the - // command "!sorry" (if '!' is your command char). - // This is not documented, I know. But one probably does - // not want that every users can bypass the flood control - // Of course, if you want this feature to remain 'secret', - // do not use it in public. - if (rest.toUpper() == String(cnx->bot->commandChar) + "SORRY") { - cnx->bot->ignoredUserhosts[fromUserhost] = 0; - from->sendNotice("\002Don't do it again!\002"); - } - return; - } - - if (rest[0] == '\001') { - rest = rest.subString(1, rest.length() - 2); - if (!Utils::isChannel(to)) - for (std::map >::iterator it = - cnx->bot->spyList.begin(); it != cnx->bot->spyList.end(); ++it) - (*it).second->sendNotice(String("CTCP From ") + nick + - ": " + rest); - Parser::parseCTCP(cnx, from, to, rest); - } - else { - if ((rest.length() < 5 || - rest.subString(1, 5).toUpper() != "IDENT") && - (rest.length() < 8 || - rest.subString(1, 8).toUpper() != "PASSWORD") && - !Utils::isChannel(to)) - for (std::map >::iterator it = - cnx->bot->spyList.begin(); it != cnx->bot->spyList.end(); ++it) - (*it).second->sendNotice(String("*") + nick + "* " + rest); - Parser::parseMessage(cnx, from, to, rest); - } -} - -void -Parser::parsePart(ServerConnection *cnx, - Person *from, String rest) -{ - String n = from->getNick(); - StringTokenizer st(rest); - String channel = st.nextToken(); - -#ifdef USESCRIPTS - cnx->bot->botInterp->RunHooks(Hook::LEAVE, n + " " + channel, - scm_listify (Utils::string2SCM(n), - Utils::string2SCM(channel), - SCM_UNDEFINED)); -#endif - - if (n.toLower() == cnx->bot->nickName.toLower()) { - cnx->bot->logLine(String("Leaved channel ") + channel + "."); - cnx->bot->channelList->delChannel(channel); - } else { - Channel * c = cnx->bot->channelList->getChannel(channel); - if (!c) return; - c->delNick(n); - if (c->countOp == 0 && c->count == 1) { - cnx->queue->sendPart(channel); - cnx->queue->sendJoin(channel, cnx->bot->wantedChannels[channel]->key); - } - } -} - -void -Parser::parsePing(ServerConnection * cnx, - Person *from, String rest) -{ - cnx->queue->sendPong(rest); -} - -void -Parser::parsePong(ServerConnection *cnx, - Person *from, String rest) -{ - cnx->lag = (cnx->lag + 2 * (time(NULL) - cnx->pingTime)) / 3; - cnx->bot->sentPing = false; -} - -void -Parser::parseQuit(ServerConnection *cnx, - Person *from, String rest) -{ - String n = from->getNick(); - -#ifdef USESCRIPTS - cnx->bot->botInterp->RunHooks(Hook::SIGNOFF, n + " " + rest, - scm_listify (Utils::string2SCM(n), - Utils::string2SCM(rest), - SCM_UNDEFINED)); -#endif - - if (n == cnx->bot->nickName) - cnx->bot->stop = true; - - for (std::map >::iterator it = - cnx->bot->channelList->begin(); - it != cnx->bot->channelList->end(); - ++it) - (*it).second->delNick(n); -} - -void -Parser::parseTopic(ServerConnection *cnx, - Person *from, String rest) -{ - StringTokenizer st(rest); - String channel = st.nextToken(); - String newTopic = st.rest().subString(1); - Channel *c = cnx->bot->channelList->getChannel(channel); - -#ifdef USESCRIPTS - cnx->bot->botInterp->RunHooks(Hook::TOPIC, from->getNick() + " " + channel + - " " + newTopic, - scm_listify (Utils::string2SCM(from->getNick()), - Utils::string2SCM(channel), - Utils::string2SCM(newTopic), - SCM_UNDEFINED)); -#endif - - if (!c) return; - - if (c->lockedTopic && from->getNick() != cnx->bot->nickName) - cnx->queue->sendTopic(channel, c->channelTopic); - - c->channelTopic = newTopic; -} - -void -Parser::parseCTCP(ServerConnection *cnx, - Person *from, String to, - String parameters) -{ - StringTokenizer st(parameters); - String command = st.nextToken().toUpper(); - String nick = from->getNick(); - String rest; - - if (st.hasMoreTokens()) - rest = st.rest(); - else - rest = ""; - -#ifdef USESCRIPTS - cnx->bot->botInterp->RunHooks(Hook::CTCP, nick + " " + to + " " + - command + " " + rest, - scm_listify (Utils::string2SCM(nick), - Utils::string2SCM(to), - Utils::string2SCM(command), - Utils::string2SCM(rest), - SCM_UNDEFINED)); -#endif - - if (command == "PING") - cnx->queue->sendCTCPReply(nick, "PING", rest); - else if (command == "VERSION") - cnx->queue->sendCTCPReply(nick, "VERSION", cnx->bot->versionString); - - else if (command == "CLOCK") { - time_t diff = time(NULL) - cnx->bot->startTime; - cnx->queue->sendCTCPReply(nick, "CLOCK", String("elapsed time: ") + - String((long)(diff / 86400)) + "d" + - String((long)(diff % 86400) / 3600) + - "h" + String((long)(diff % 3600) / 60) + - "m" + String((long)(diff % 60)) + "s"); - } else if (command == "COMMAND") - cnx->queue->sendCTCPReply(nick, - "COMMAND", - String(cnx->bot->commandChar)); - else if (command == "LAG") - cnx->queue->sendCTCPReply(nick, "LAG", - String((long)cnx->lag) + " second(s)"); - else if (command == "DCC") { - StringTokenizer st2(rest); - command = st2.nextToken().toUpper(); - if (command == "CHAT") { - // FIXME: Re-activate and debug DCC -// st2.nextToken(); -// unsigned long address = -// htonl(strtoul((const char *)st2.nextToken(), 0, 0)); -// int port = atoi((const char *)st2.rest()); -// if (port >= 1024 && Utils::getLevel(cnx->bot, from->getAddress())) -// cnx->bot->addDCC(from, address, port); - } - } -#ifdef USESCRIPTS - else if (command == "ACTION") { - cnx->bot->botInterp->RunHooks(Hook::ACTION, from->getAddress() + " " + - to + " " + rest, - scm_listify (Utils::string2SCM(from->getAddress()), - Utils::string2SCM(to), - Utils::string2SCM(rest), - SCM_UNDEFINED)); - } -#endif -} - -struct userFunctionsStruct userFunctionsInit[] = - -{ - { "ACTION", UserCommands::Action, User::USER, true }, - { "ADDUSER", UserCommands::AddUser, User::FRIEND, false }, - { "ADDSERVER", UserCommands::AddServer, User::FRIEND, false }, - { "ADDSHIT", UserCommands::AddShit, User::FRIEND, false }, - { "ALIAS", UserCommands::Alias, User::MASTER, false }, - { "BAN", UserCommands::Ban, User::USER, true }, - { "BANLIST", UserCommands::BanList, User::USER, true }, - // { "CHANGELEVEL", UserCommands::ChangeLevel, User::FRIEND, false }, - { "CHANNELS", UserCommands::Channels, User::FRIEND, false }, - { "CYCLE", UserCommands::Cycle, User::FRIEND, true }, - { "DCCLIST", UserCommands::DCCList, User::FRIEND, false }, - { "DEBAN", UserCommands::Deban, User::USER, true }, - { "DELSERVER", UserCommands::DelServer, User::FRIEND, false }, - { "DELUSER", UserCommands::DelUser, User::FRIEND, false }, - { "DELSHIT", UserCommands::DelShit, User::FRIEND, false }, - { "DEOP", UserCommands::Deop, User::TRUSTED_USER, true }, - { "DIE", UserCommands::Die, User::MASTER, false }, - { "DO", UserCommands::Do, User::MASTER, false }, -#ifdef USESCRIPTS - { "EXECUTE", UserCommands::Execute, User::MASTER, false }, -#endif - { "HELP", UserCommands::Help, User::NONE, false }, - { "IDENT", UserCommands::Ident, User::NONE, true }, - { "INVITE", UserCommands::Invite, User::USER, true }, - { "JOIN", UserCommands::Join, User::FRIEND, false }, - { "KEEP", UserCommands::Keep, User::FRIEND, true }, - { "KICK", UserCommands::Kick, User::USER, true }, - { "KICKBAN", UserCommands::KickBan, User::USER, true }, - { "LOAD", UserCommands::Load, User::FRIEND, false }, -#ifdef USESCRIPTS - { "LOADSCRIPT", UserCommands::LoadScript, User::MASTER, false }, -#endif - { "LOCK", UserCommands::Lock, User::FRIEND, true }, - { "MODE", UserCommands::Mode, User::FRIEND, true }, - { "MSG", UserCommands::Msg, User::USER, false }, - { "NAMES", UserCommands::Names, User::USER, true }, - { "NEXTSERVER", UserCommands::NextServer, User::FRIEND, false }, - { "NICK", UserCommands::Nick, User::FRIEND, false }, - { "NSLOOKUP", UserCommands::NsLookup, User::USER, false }, - { "OP", UserCommands::Op, User::TRUSTED_USER, true }, - { "PART", UserCommands::Part, User::FRIEND, true }, - { "PASSWORD", UserCommands::Password, User::USER, true }, - { "RECONNECT", UserCommands::Reconnect, User::FRIEND, false }, - { "RSPYMESSAGE", UserCommands::RSpyMessage, User::USER, false }, - { "SAVE", UserCommands::Save, User::FRIEND, false }, - { "SAY", UserCommands::Say, User::USER, true }, - { "SERVER", UserCommands::Server, User::FRIEND, false }, - { "SERVERLIST", UserCommands::ServerList, User::FRIEND, false }, - { "SETVERSION", UserCommands::SetVersion, User::MASTER, false }, - { "SHITLIST", UserCommands::ShitList, User::FRIEND, false }, - { "SPYLIST", UserCommands::SpyList, User::USER, false }, - { "SPYMESSAGE", UserCommands::SpyMessage, User::USER, false }, - { "STATS", UserCommands::Stats, User::FRIEND, true }, - { "TBAN", UserCommands::TBan, User::USER, true }, - { "TKBAN", UserCommands::TKBan, User::USER, true }, - { "TOPIC", UserCommands::Topic, User::USER, true }, - { "UNLOCK", UserCommands::Unlock, User::FRIEND, true }, - { "USERLIST", UserCommands::UserList, User::FRIEND, false }, - { "WHO", UserCommands::Who, User::NONE, true }, - { "WHOIS", UserCommands::Whois, User::FRIEND, true }, - { "", 0, 0, false } -}; - -void -Parser::parseMessage(ServerConnection *cnx, - Person *from, String to, - String parameters) -{ -#ifdef USESCRIPTS - if (Utils::isChannel(to)) - cnx->bot->botInterp->RunHooks(Hook::PUBLIC, from->getNick() + " " + - to + " " + parameters, - scm_listify (Utils::string2SCM(from->getNick()), - Utils::string2SCM(to), - Utils::string2SCM(parameters), - SCM_UNDEFINED)); - else - cnx->bot->botInterp->RunHooks(Hook::MESSAGE, from->getNick() + " " + parameters, - scm_listify (Utils::string2SCM(from->getNick()), - Utils::string2SCM(parameters), - SCM_UNDEFINED)); -#endif - - if (parameters[0] != cnx->bot->commandChar) - return; - - StringTokenizer st(parameters); - - String command = st.nextToken().subString(1).toUpper(); - String rest = st.rest().trim(); - int level; - bool identified = false; - - std::list::iterator it; - for (it = cnx->bot->userFunctions.begin(); - it != cnx->bot->userFunctions.end(); - ++it) - if (command == (*it)->name) { - if ((*it)->needsChannelName) { - if (Utils::isChannel(rest)) { - StringTokenizer st2(rest); - to = st.nextToken(); - rest = st.rest(); - } - if (!Utils::isChannel(to)) { - from->sendNotice("\002You need to supply a channel name" - " for this command\002"); - return; - } - if (!cnx->bot->channelList->getChannel(to)) { - from->sendNotice(String("\002I am not on channel\002 ") + - to); - return; - } - level = Utils::getLevel(cnx->bot, from->getAddress(), to); - User * u = 0; - if (Channel *c = cnx->bot->channelList->getChannel(to)) - u = c->getUser(from->getNick()); - if (!u || !u->userListItem) - identified = true; - else - identified = u->userListItem->passwd == "" || u->userListItem->identified > 0; - } else { - level = Utils::getLevel(cnx->bot, from->getAddress()); - identified = true; - } - if (level >= (*it)->minLevel) { - cnx->bot->logLine(from->getAddress() + " did " + command + - " " + rest); -#ifdef USESCRIPTS - if ((*it)->argsCount != -1) { - Parser::parseScriptFunction(cnx, to, (*it)->needsChannelName, - (*it)->scmFunc, (*it)->argsCount, rest); - } else { - (*it)->function(cnx, from, to, rest); - } -#else - (*it)->function(cnx, from, to, rest); -#endif - break; - } else { - if (!identified) - from->sendNotice(String("\002You are not identified on channel\002 ")+to); - } - } -} - -#ifdef USESCRIPTS -void -Parser::parseScriptFunction(ServerConnection *cnx, String channel, - bool needsChannelName, SCM scmFunc, - int argsCount, String parameters) -{ - String param; - SCM args_list = scm_listify (SCM_UNDEFINED); - - if (needsChannelName) { - args_list = gh_append2(args_list, - scm_listify (Utils::string2SCM(channel), - SCM_UNDEFINED)); - argsCount--; - } - - StringTokenizer st(parameters); - for (int i = argsCount; i > 0; i--) { - if (i == 1) - param = st.rest(); - else - param = st.nextToken(); - args_list = gh_append2(args_list, - scm_listify (Utils::string2SCM(param), - SCM_UNDEFINED)); - } - - struct wrapper_data wd; - wd.func = scmFunc; - wd.args = args_list; - - gh_catch(SCM_BOOL_T, (scm_catch_body_t) scm_apply_wrapper, - (void *)&wd, (scm_catch_handler_t) Interp::ErrorHandler, - 0); -} -#endif +// Parser.C -*- C++ -*- +// Copyright (c) 1997, 1998 Etienne BERNARD +// Copyright (C) 2002,2003,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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "StringTokenizer.H" +#include "Parser.H" +#include "UserCommands.H" +#include "Macros.H" +#include "Utils.H" +#include "ShitList.H" + +typedef void (*fptr) (ServerConnection *, Person *, String); +std::map < std::string, fptr, std::less < std::string > >Parser::functions; + +void +Parser::init () +{ + // Parser functions + Parser::functions["001"] = Parser::parse001; /* RPL_WELCOME */ + Parser::functions["302"] = Parser::parse302; /* RPL_USERHOST */ + Parser::functions["311"] = Parser::parse311; /* RPL_WHOISUSER */ + Parser::functions["315"] = Parser::parse315; /* RPL_ENDOFWHO */ + Parser::functions["324"] = Parser::parse324; /* RPL_CHANNELMODEIS */ + Parser::functions["332"] = Parser::parse332; /* RPL_TOPIC */ + Parser::functions["352"] = Parser::parse352; /* RPL_WHOREPLY */ + Parser::functions["353"] = Parser::parse353; /* RPL_NAMESREPLY */ + Parser::functions["366"] = Parser::parse366; /* RPL_ENDOFNAMES */ + Parser::functions["367"] = Parser::parse367; /* RPL_BANLIST */ + Parser::functions["401"] = Parser::parse401; /* ERR_NOSUCHNICK */ + Parser::functions["433"] = Parser::parse433; /* ERR_NICKNAMEINUSE */ + Parser::functions["437"] = Parser::parse433; /* ERR_UNAVAILRESOURCE */ + Parser::functions["471"] = Parser::parse473; /* ERR_CHANNELISFULL */ + Parser::functions["473"] = Parser::parse473; /* ERR_INVITEONLYCHAN */ + Parser::functions["474"] = Parser::parse473; /* ERR_BANNEDFROMCHAN */ + Parser::functions["475"] = Parser::parse473; /* ERR_BADCHANNELKEY */ + Parser::functions["ERROR"] = Parser::parseError; + Parser::functions["INVITE"] = Parser::parseInvite; + Parser::functions["JOIN"] = Parser::parseJoin; + Parser::functions["KICK"] = Parser::parseKick; + Parser::functions["MODE"] = Parser::parseMode; + Parser::functions["NICK"] = Parser::parseNick; + Parser::functions["NOTICE"] = Parser::parseNotice; + Parser::functions["PART"] = Parser::parsePart; + Parser::functions["PING"] = Parser::parsePing; + Parser::functions["PONG"] = Parser::parsePong; + Parser::functions["PRIVMSG"] = Parser::parsePrivmsg; + Parser::functions["QUIT"] = Parser::parseQuit; + Parser::functions["TOPIC"] = Parser::parseTopic; + Parser::functions[""] = Parser::parseError; +} + + +void +Parser::parseLine (ServerConnection * cnx, String line) +{ + StringTokenizer st (line); + Person *from = 0; +#ifdef USESCRIPTS + cnx->bot->botInterp->RunHooks (Hook::RAW, line, + scm_listify (Utils:: + str2scm (line), + SCM_UNDEFINED)); +#endif + if (line[0] == ':') + { + String fromMask = st.next_token ().substr (1); + if (fromMask.find ('!') != -1) + from = new Person (cnx->bot, fromMask); + } + + String command = st.next_token (); + String rest = st.rest (); + + // We must use map<>::find or else a new entry will be created in + // the map which will cause another lookup of the same invalid + // command to segfault the bot + std::map >::const_iterator cit + = functions.find (command); + + if (cit != functions.end ()) + { + fptr temp_func = cit->second; + temp_func (cnx, from, rest); + } + + delete from; +} + +void +Parser::parse001 (ServerConnection * cnx, Person * from, String rest) +{ + String temp = ""; + StringTokenizer st (rest); + String realNick = st.next_token (); + if ((cnx->bot->nickName).toLower () != realNick.toLower ()) + { + // Yes, this can happen, and it was a very subtle bug + cnx->bot->nickName = realNick; + cnx->bot->userList->removeFirst (); + cnx->bot->userList->addUserFirst (realNick + "!" + + cnx->bot->userHost, "*", 0, + 3, true, -1, ""); + cnx->bot->lastNickNameChange = time (0); + cnx->bot->rehash (); + } + + cnx->bot->connected = true; + cnx->queue->sendUserMode (cnx->bot->nickName, "+i"); + cnx->queue->sendWhois (cnx->bot->nickName); + for (std::map < String, wantedChannel *, + std::less < String > >::iterator it = + cnx->bot->wantedChannels.begin (); + it != cnx->bot->wantedChannels.end (); ++it) + cnx->queue->sendJoin ((*it).first, (*it).second->key); + cnx->bot->logLine (String ("Connected to server ") + + cnx->bot->serverList->currentServer ()-> + getHostName () + " (" + + String ((long) cnx->bot->serverList-> + currentServer ()->getPort ()) + ")."); +} + +void +Parser::parse302 (ServerConnection * cnx, Person * from, String rest) +{ + unsigned long num = cnx->bot->receivedUserhostID++; + StringTokenizer st (rest); + st.next_token (':'); + if (st.rest ().length ()) + { + st.next_token ('='); + String parameters = st.rest (); + parameters = parameters.substr (1); + cnx->bot->userhostMap[num] = parameters; + } + else + cnx->bot->userhostMap[num] = ""; +} + +void +Parser::parse311 (ServerConnection * cnx, Person * from, String rest) +{ + StringTokenizer st (rest); + st.next_token (); + String nuh = st.next_token () + "!"; + String uh = st.next_token () + "@"; + uh = uh + st.next_token (); + nuh = nuh + uh; + cnx->bot->userList->addUserFirst (nuh, "*", 0, 3, true, -1, ""); + cnx->bot->userHost = uh; +} + +void +Parser::parse315 (ServerConnection * cnx, Person * from, String rest) +{ + StringTokenizer st (rest); + st.next_token (); + String channel = st.next_token (); + Channel *c = cnx->bot->channelList->getChannel (channel); + if (!c) + return; + c->gotWho = true; +} + +void +Parser::parse324 (ServerConnection * cnx, Person * from, String rest) +{ + StringTokenizer st (rest); + st.next_token (); + String channel = st.next_token (); + if (Channel * c = cnx->bot->channelList->getChannel (channel)) + if (c) + c->parseMode (from, st.rest ()); +} + +void +Parser::parse332 (ServerConnection * cnx, Person * from, String rest) +{ + StringTokenizer st (rest); + st.next_token (); + String channel = st.next_token (); + if (Channel * c = cnx->bot->channelList->getChannel (channel)) + if (c) + c->channelTopic = st.rest ().substr (1); +} + +void +Parser::parse352 (ServerConnection * cnx, Person * from, String rest) +{ + StringTokenizer st (rest); + st.next_token (); + String ch = st.next_token (); + String uh = st.next_token () + "@"; + uh = uh + st.next_token (); + st.next_token (); + String n = st.next_token (); + String m = st.next_token (); + int mode = 0; + for (int i = 0; i < m.length (); i++) + switch (m[i]) + { + case 'H': + break; + case 'G': + mode |= User::AWAY_MODE; + break; + case '*': + mode |= User::IRCOP_MODE; + break; + case '@': + mode |= User::OP_MODE; + break; + case '+': + mode |= User::VOICE_MODE; + break; + } + if (Channel * c = cnx->bot->channelList->getChannel (ch)) + if (c) + c->addNick (n, uh, mode, cnx->bot->userList); +} + +void +Parser::parse353 (ServerConnection * cnx, Person * from, String rest) +{ + int mode = 0; + String nick; + StringTokenizer st (rest); + st.next_token (); + st.next_token (); + Channel *c = cnx->bot->channelList->getChannel (st.next_token ()); + if (!c) + return; + StringTokenizer st2 (st.next_token (':')); + while (st2.more_tokens_p ()) + { + nick = st2.next_token (); + if (nick[0] == '@') + { + mode = User::OP_MODE; + nick = nick.substr (1); + } + else if (nick[0] == '+') + { + mode = User::VOICE_MODE; + nick = nick.substr (1); + } + c->addNick (nick, "", mode, 0, true); + } +} + +void +Parser::parse366 (ServerConnection * cnx, Person * from, String rest) +{ + StringTokenizer st (rest); + st.next_token (); + String ch = st.next_token (); + if (Channel * c = cnx->bot->channelList->getChannel (ch)) + c->joined = true; +} + +void +Parser::parse367 (ServerConnection * cnx, Person * from, String rest) +{ + StringTokenizer st (rest); + st.next_token (); + String ch = st.next_token (); + if (Channel * c = cnx->bot->channelList->getChannel (ch)) + c->addBan (st.next_token (), -1); +} + +void +Parser::parse401 (ServerConnection * cnx, Person * from, String rest) +{ + StringTokenizer st (rest); + st.next_token (); + String nick = st.next_token (); + if (cnx->bot->spyList.find (nick) != cnx->bot->spyList.end ()) + { + delete cnx->bot->spyList[nick]; + cnx->bot->spyList.erase (nick); + } +} + +void +Parser::parse433 (ServerConnection * cnx, Person * from, String rest) +{ + if (cnx->bot->connected) + return; + if (cnx->bot->nickName.length () == 9) + { + int i; + for (i = 0; + i < cnx->bot->nickName.length () + && cnx->bot->nickName[i] == '_'; i++); + if (i < cnx->bot->nickName.length ()) + cnx->bot->nickName = + cnx->bot->nickName.substr (0, + i - 1) + "_" + + cnx->bot->nickName.substr (i + 1); + else + cnx->bot->nickName = cnx->bot->nickName.substr (0, 4) + + String ((long) (rand () % 10000)); + } + else + cnx->bot->nickName = cnx->bot->nickName + "_"; + cnx->queue->sendNick (cnx->bot->nickName); +} + +void +Parser::parse473 (ServerConnection * cnx, Person * from, String rest) +{ + StringTokenizer st (rest); + st.next_token (); + cnx->bot->logLine (String ("Unable to join channel ") + + st.next_token () + "."); +} + +void +Parser::parseError (ServerConnection * cnx, Person * from, String rest) +{ + cnx->bot->logLine (String ("Error from server ") + + cnx->bot->serverList->currentServer ()-> + getHostName () + " (" + + String ((long) cnx->bot->serverList-> + currentServer ()->getPort ()) + ")."); + cnx->bot->nextServer (); +} + +void +Parser::parseInvite (ServerConnection * cnx, Person * from, String rest) +{ + String nick = from->getNick (); + StringTokenizer st (rest); + st.next_token (':'); + String channel = st.rest (); +#ifdef USESCRIPTS + cnx->bot->botInterp->RunHooks (Hook::INVITE, + nick + " " + channel, + scm_listify (Utils:: + str2scm (nick), + Utils:: + str2scm + (channel), SCM_UNDEFINED)); +#endif + if (cnx->bot->wantedChannels.find (channel) != + cnx->bot->wantedChannels.end ()) + cnx->queue->sendJoin (channel, cnx->bot->wantedChannels[channel]->key); +} + +void +Parser::parseJoin (ServerConnection * cnx, Person * from, String rest) +{ + StringTokenizer st (from->getAddress ()); + String n = st.next_token ('!'); + String uh = st.next_token (); + StringTokenizer st2 (rest); + String c = st2.next_token (':'); + String mode; + bool joinAndMode = false; +#ifdef USESCRIPTS + cnx->bot->botInterp->RunHooks (Hook::JOIN, n + " " + c, + scm_listify (Utils:: + str2scm (n), + Utils:: + str2scm (c), SCM_UNDEFINED)); +#endif + // This part of code is for the combined JOIN & MODE of ircd 2.9 + if (c.find ('\007') >= 0) + { + joinAndMode = true; + StringTokenizer st3 (c); + c = st3.next_token ('\007'); + String m = st3.rest (); + mode = c + " +" + m; + for (int i = 0; i < m.length (); i++) + mode = mode + " " + n; + } + + if (n == cnx->bot->nickName) + { + cnx->bot->logLine (String ("Joined channel ") + c + "."); + if (cnx->bot->wantedChannels.find (c) != + cnx->bot->wantedChannels.end ()) + cnx->bot->channelList-> + addChannel (cnx, c, cnx->bot->wantedChannels[c]->keep); + else + cnx->bot->channelList->addChannel (cnx, c); + cnx->queue->sendWho (c); + cnx->queue->sendChannelMode (String ("MODE ") + c + " b"); + cnx->queue->sendChannelMode (String ("MODE ") + c); + } + else + { + Channel *ch = cnx->bot->channelList->getChannel (c); + if (!ch) + return; + ShitEntry *se = cnx->bot->shitList->getShit (n + "!" + uh, c); + if (se && se->isStillValid () && + se->getShitLevel () >= ShitEntry::SHIT_NOJOIN) + { + cnx->queue->sendChannelMode (c, "+b", se->getMask ()); + cnx->queue->sendKick (c, n, se->getShitReason ()); + return; + } + ch->addNick (n, uh, 0, cnx->bot->userList); + if (ch->getUser (n)->getAop () + && !(ch->getUser (n)->mode & User::OP_MODE) && cnx->bot->iAmOp (c)) + { + // This is a part of the antispoof code + ch->getUser (n)->userkey = Utils::get_key (); + Commands::CTCP (cnx->bot, n, "PING", + ch->getUser (n)->userkey + " " + c); + } + } + + if (joinAndMode) + parseMode (cnx, 0, mode); +} + +void +Parser::parseKick (ServerConnection * cnx, Person * from, String rest) +{ + StringTokenizer st (rest); + String channel = st.next_token (); + String target = st.next_token (); + String reason = st.rest ().substr (1); +#ifdef USESCRIPTS + cnx->bot->botInterp->RunHooks (Hook::KICK, + target + " " + + from->getNick () + " " + + channel + " " + reason, + scm_listify (Utils:: + str2scm + (target), + Utils:: + str2scm (from-> + getNick + ()), + Utils:: + str2scm + (channel), + Utils:: + str2scm + (reason), SCM_UNDEFINED)); +#endif + if (target == cnx->bot->nickName) + { + cnx->bot->logLine (from->getAddress () + + " kicked me out of channel " + channel + + " (" + reason + ")."); + cnx->queue->sendJoin (channel, + cnx->bot->channelList-> + getChannel (channel)->channelKey); + cnx->bot->channelList->delChannel (channel); + } + else + { + if (!cnx->bot->channelList->getChannel (channel)) + return; + User *u = cnx->bot->channelList->getChannel (channel)->getUser (target); + if (u && u->getProt () >= User::NO_KICK) + { + String fromNick = from->getNick (); + User *v = + cnx->bot->channelList->getChannel (channel)->getUser (fromNick); + if (v->getProt () < User::NO_KICK) + { + cnx->bot->logLine (from->getAddress () + " kicked " + target + + " (protected) out of channel " + channel + + " (" + reason + ")."); + cnx->queue->sendKick (channel, fromNick, + target + " \002is protected !\002"); + } + } + cnx->bot->channelList->getChannel (channel)->delNick (target); + } +} + +void +Parser::parseMode (ServerConnection * cnx, Person * from, String rest) +{ + StringTokenizer st (rest); + String ch = st.next_token (); + String modes = st.rest (); +#ifdef USESCRIPTS + if (from) + cnx->bot->botInterp->RunHooks (Hook::MODE, + from->getNick () + " " + ch + + " " + modes, + scm_listify (Utils:: + str2scm (from-> + getNick + ()), + Utils:: + str2scm (ch), + Utils:: + str2scm (modes), + SCM_UNDEFINED)); +#endif + if (Utils::channel_p (ch)) + { + Channel *c = cnx->bot->channelList->getChannel (ch); + if (!c) + return; + if (from) + c->parseMode (from, modes); + else + c->parseMode (0, modes); + } +} + +void +Parser::parseNick (ServerConnection * cnx, Person * from, String rest) +{ + String on_orig = from->getNick (); + String on = on_orig.toLower (); + String nn = rest.substr (1); + String nn_lower = nn.toLower (); +#ifdef USESCRIPTS + cnx->bot->botInterp->RunHooks (Hook::NICKNAME, + on_orig + " " + nn, + scm_listify (Utils:: + str2scm + (on_orig), + Utils:: + str2scm (nn), + SCM_UNDEFINED)); +#endif + if ((cnx->bot->nickName).toLower () == on) + { + cnx->bot->userList->removeFirst (); + cnx->bot->userList->addUserFirst (nn + "!" + + cnx->bot->userHost, "*", 0, + 3, true, -1, ""); + cnx->bot->lastNickNameChange = time (0); + cnx->bot->nickName = nn; + cnx->bot->rehash (); + } + + if (cnx->bot->spyList.find (on) != cnx->bot->spyList.end ()) + { + cnx->bot->spyList[nn_lower] = cnx->bot->spyList[on]; + cnx->bot->spyList.erase (on); + } + + for (std::map < String, Channel *, + std::less < String > >::iterator it = + cnx->bot->channelList->begin (); + it != cnx->bot->channelList->end (); ++it) + if ((*it).second->hasNick (on)) + (*it).second->changeNick (on, nn_lower); +} + +void +Parser::parseNotice (ServerConnection * cnx, Person * from, String rest) +{ + String nick = ""; + if (from) + nick = from->getNick (); + StringTokenizer st (rest); + String to = st.next_token (); + rest = st.rest ().substr (1); + if (rest[0] != '\001') + { +#ifdef USESCRIPTS + if (Utils::channel_p (to)) + cnx->bot->botInterp->RunHooks (Hook::PUBLIC_NOTICE, + nick + " " + to + " " + rest, + scm_listify (Utils:: + str2scm (nick), + Utils:: + str2scm (to), + Utils:: + str2scm (rest), + SCM_UNDEFINED)); + else + cnx->bot->botInterp->RunHooks (Hook::NOTICE, nick + " " + rest, + scm_listify (Utils:: + str2scm (nick), + Utils:: + str2scm (rest), + SCM_UNDEFINED)); +#endif + return; + } + + rest = rest.substr (1, rest.length () - 2); + StringTokenizer st2 (rest); + String command = st2.next_token (); + rest = st2.rest (); +#ifdef USESCRIPTS + cnx->bot->botInterp->RunHooks (Hook::CTCP_REPLY, + nick + " " + command + " " + + rest, + scm_listify (Utils:: + str2scm (nick), + Utils:: + str2scm + (command), + Utils:: + str2scm (rest), + SCM_UNDEFINED)); +#endif + if (command == "PING") + { + StringTokenizer st3 (rest); + rest = st3.next_token (); + String c = st3.rest (); + if (cnx->bot->channelList->getChannel (c) && + cnx->bot->channelList->getChannel (c)->getUser (nick) && + cnx->bot->channelList->getChannel (c)->getUser (nick)-> + getAop () + && !(cnx->bot->channelList->getChannel (c)-> + getUser (nick)->mode & User::OP_MODE) + && cnx->bot->channelList->getChannel (c)->getUser (nick)-> + userkey == rest) + cnx->queue->sendChannelMode (c, "+o", nick); + } +} + +void +Parser::parsePrivmsg (ServerConnection * cnx, Person * from, String rest) +{ + String nick = from->getNick (); + StringTokenizer st (rest); + String to = st.next_token (); + String fromUserhost = Utils::get_userhost (from->getAddress ()); + rest = st.rest ().substr (1); + if (++(cnx->bot->ignoredUserhosts[fromUserhost]) > Bot::MAX_MESSAGES) + { + if (cnx->bot->ignoredUserhosts[fromUserhost] == Bot::MAX_MESSAGES + 1) + { +#ifdef USESCRIPTS + cnx->bot->botInterp->RunHooks (Hook::FLOOD, nick, + scm_listify (Utils:: + str2scm (nick), + SCM_UNDEFINED)); +#endif + cnx->bot->ignoredUserhosts[fromUserhost] += Bot::IGNORE_DELAY; + cnx->bot->logLine (from->getAddress () + + " is flooding me. We will ignore him/her/it."); + if (!Utils::channel_p (to)) + from-> + sendNotice (String ("\002You are now being ignored for ") + + String ((long) Bot::IGNORE_DELAY) + + " seconds.\002"); + } + // The following lines reset the counter if you use the + // command "!sorry" (if '!' is your command char). + // This is not documented, I know. But one probably does + // not want that every users can bypass the flood control + // Of course, if you want this feature to remain 'secret', + // do not use it in public. + if (rest.toUpper () == String (cnx->bot->commandChar) + "SORRY") + { + cnx->bot->ignoredUserhosts[fromUserhost] = 0; + from->sendNotice ("\002Don't do it again!\002"); + } + return; + } + + if (rest[0] == '\001') + { + rest = rest.substr (1, rest.length () - 2); + if (!Utils::channel_p (to)) + for (std::map < String, Person *, + std::less < String > >::iterator it = + cnx->bot->spyList.begin (); it != cnx->bot->spyList.end (); ++it) + (*it).second->sendNotice (String ("CTCP From ") + + nick + ": " + rest); + Parser::parseCTCP (cnx, from, to, rest); + } + else + { + if ((rest.length () < 5 || + rest.substr (1, 5).toUpper () != "IDENT") && + (rest.length () < 8 || + rest.substr (1, 8).toUpper () != "PASSWORD") && + !Utils::channel_p (to)) + for (std::map < String, Person *, + std::less < String > >::iterator it = + cnx->bot->spyList.begin (); it != cnx->bot->spyList.end (); ++it) + (*it).second->sendNotice (String ("*") + nick + "* " + rest); + Parser::parseMessage (cnx, from, to, rest); + } +} + +void +Parser::parsePart (ServerConnection * cnx, Person * from, String rest) +{ + String n = from->getNick (); + StringTokenizer st (rest); + String channel = st.next_token (); +#ifdef USESCRIPTS + cnx->bot->botInterp->RunHooks (Hook::LEAVE, n + " " + channel, + scm_listify (Utils:: + str2scm (n), + Utils:: + str2scm + (channel), SCM_UNDEFINED)); +#endif + if (n.toLower () == cnx->bot->nickName.toLower ()) + { + cnx->bot->logLine (String ("Leaved channel ") + channel + "."); + cnx->bot->channelList->delChannel (channel); + } + else + { + Channel *c = cnx->bot->channelList->getChannel (channel); + if (!c) + return; + c->delNick (n); + if (c->countOp == 0 && c->count == 1) + { + cnx->queue->sendPart (channel); + cnx->queue->sendJoin (channel, + cnx->bot->wantedChannels[channel]->key); + } + } +} + +void +Parser::parsePing (ServerConnection * cnx, Person * from, String rest) +{ + cnx->queue->sendPong (rest); +} + +void +Parser::parsePong (ServerConnection * cnx, Person * from, String rest) +{ + cnx->lag = (cnx->lag + 2 * (time (NULL) - cnx->pingTime)) / 3; + cnx->bot->sentPing = false; +} + +void +Parser::parseQuit (ServerConnection * cnx, Person * from, String rest) +{ + String n = from->getNick (); +#ifdef USESCRIPTS + cnx->bot->botInterp->RunHooks (Hook::SIGNOFF, n + " " + rest, + scm_listify (Utils:: + str2scm (n), + Utils:: + str2scm (rest), + SCM_UNDEFINED)); +#endif + if (n == cnx->bot->nickName) + cnx->bot->stop = true; + for (std::map < String, Channel *, + std::less < String > >::iterator it = + cnx->bot->channelList->begin (); + it != cnx->bot->channelList->end (); ++it) + (*it).second->delNick (n); +} + +void +Parser::parseTopic (ServerConnection * cnx, Person * from, String rest) +{ + StringTokenizer st (rest); + String channel = st.next_token (); + String newTopic = st.rest ().substr (1); + Channel *c = cnx->bot->channelList->getChannel (channel); +#ifdef USESCRIPTS + cnx->bot->botInterp->RunHooks (Hook::TOPIC, + from->getNick () + " " + + channel + " " + newTopic, + scm_listify (Utils:: + str2scm (from-> + getNick + ()), + Utils:: + str2scm + (channel), + Utils:: + str2scm + (newTopic), SCM_UNDEFINED)); +#endif + if (!c) + return; + if (c->lockedTopic && from->getNick () != cnx->bot->nickName) + cnx->queue->sendTopic (channel, c->channelTopic); + c->channelTopic = newTopic; +} + +void +Parser::parseCTCP (ServerConnection * cnx, + Person * from, String to, String parameters) +{ + StringTokenizer st (parameters); + String command = Utils::to_upper (st.next_token ()); + String nick = from->getNick (); + String rest; + if (st.more_tokens_p ()) + rest = st.rest (); + else + rest = ""; +#ifdef USESCRIPTS + cnx->bot->botInterp->RunHooks (Hook::CTCP, + nick + " " + to + " " + + command + " " + rest, + scm_listify (Utils:: + str2scm (nick), + Utils:: + str2scm (to), + Utils:: + str2scm + (command), + Utils:: + str2scm (rest), + SCM_UNDEFINED)); +#endif + if (command == "PING") + { + Commands::CTCPReply (cnx->bot, nick, "PING", rest); + } + else if (command == "VERSION") + { + Commands::CTCPReply (cnx->bot, nick, "VERSION", + cnx->bot->versionString); + } + else if (command == "CLOCK") + { + time_t diff = time (NULL) - cnx->bot->startTime; + Commands::CTCPReply (cnx->bot, nick, "CLOCK", + String ("elapsed time: ") + + String ((long) (diff / 86400)) + + "d" + + String ((long) (diff % 86400) / + 3600) + "h" + + String ((long) (diff % 3600) / 60) + + "m" + String ((long) (diff % 60)) + "s"); + } + else if (command == "COMMAND") + { + Commands::CTCPReply (cnx->bot, nick, + "COMMAND", String (cnx->bot->commandChar)); + } + else if (command == "LAG") + { + Commands::CTCPReply (cnx->bot, nick, "LAG", + String ((long) cnx->lag) + " second(s)"); + } + else if (command == "DCC") + { + StringTokenizer st2 (rest); + command = Utils::to_upper (st2.next_token ()); + if (command == "CHAT") + { + // FIXME: debug DCC + st2.next_token (); + unsigned long address = + std::strtoul (st2.next_token ().c_str(), 0, 0); + int port = std::atoi (st2.next_token().c_str()); + if (port >= 1024 && Utils::get_level (cnx->bot, from->getAddress ())) + cnx->bot->addDCC (from, address, port, Bot::CHAT); + else + cnx->bot->logLine ("DCC Chat Failed in Parser"); + } + } +#ifdef USESCRIPTS + else if (command == "ACTION") + { + cnx->bot->botInterp->RunHooks (Hook::ACTION, + from->getNick () + " " + to + + " " + rest, + scm_listify (Utils:: + str2scm (from-> + getNick + ()), + Utils:: + str2scm (to), + Utils:: + str2scm (rest), + SCM_UNDEFINED)); + } +#endif +} + +void +Parser::parseMessage (ServerConnection * cnx, + Person * from, String to, String parameters) +{ +#ifdef USESCRIPTS + if (Utils::channel_p (to)) + cnx->bot->botInterp->RunHooks (Hook::PUBLIC, + from->getNick () + " " + to + + " " + parameters, + scm_listify (Utils:: + str2scm (from-> + getNick + ()), + Utils:: + str2scm (to), + Utils:: + str2scm + (parameters), SCM_UNDEFINED)); + else + cnx->bot->botInterp->RunHooks (Hook::MESSAGE, + from->getNick () + " " + + parameters, + scm_listify (Utils:: + str2scm (from-> + getNick + ()), + Utils:: + str2scm + (parameters), SCM_UNDEFINED)); +#endif + if (parameters[0] != cnx->bot->commandChar) + return; + + StringTokenizer st (parameters); + String command = Utils::to_upper (st.next_token ().substr (1)); + String rest = Utils::trim_str (st.rest ()); + int level; + bool identified = false; + std::map >::const_iterator uf_iter + = cnx->bot->userFunctions.find (command); + userFunction * f = 0; + + if (uf_iter != cnx->bot->userFunctions.end ()) + f = uf_iter->second; + else + return; + + if (f) + { + if (f->needsChannelName) + { + if (Utils::channel_p (rest)) + { + StringTokenizer st2 (rest); + to = st.next_token (); + rest = st.rest (); + } + if (!Utils::channel_p (to)) + { + from->sendNotice ("\002You need to supply a channel name" + " for this command\002"); + return; + } + if (!cnx->bot->channelList->getChannel (to)) + { + from->sendNotice (String ("\002I am not on channel\002 ") + + to); + return; + } + level = Utils::get_level (cnx->bot, from->getAddress (), to); + User *u = 0; + if (Channel * c = cnx->bot->channelList->getChannel (to)) + u = c->getUser (from->getNick ()); + if (!u || !u->userListItem) + identified = true; + else + identified = u->userListItem->passwd == "" + || u->userListItem->identified > 0; + } + else + { + level = Utils::get_level (cnx->bot, from->getAddress ()); + identified = true; + } + if (level >= f->minLevel) + { + cnx->bot->logLine (from->getAddress () + " did " + command + + " " + rest); +#ifdef USESCRIPTS + if (f->argsCount != -1) + { + Parser::parseScriptFunction (cnx, to, f->needsChannelName, + f->scmFunc, f->argsCount, + rest); + } + else + { + f->function (cnx, from, to, rest); + } +#else + f->function (cnx, from, to, rest); +#endif + } + else + { + if (!identified) + from-> + sendNotice (String + ("\002You are not identified on channel\002 ") + + to); + } + } +} + +#ifdef USESCRIPTS +void +Parser::parseScriptFunction (ServerConnection * cnx, + String channel, + bool needsChannelName, + SCM scmFunc, int argsCount, String parameters) +{ + String param; + SCM args_list = scm_listify (SCM_UNDEFINED); + if (needsChannelName) + { + args_list = gh_append2 (args_list, + scm_listify (Utils:: + str2scm (channel), + SCM_UNDEFINED)); + argsCount--; + } + + StringTokenizer st (parameters); + for (int i = argsCount; i > 0; i--) + { + if (i == 1) + param = st.rest (); + else + param = st.next_token (); + args_list = gh_append2 (args_list, + scm_listify (Utils::str2scm (param), + SCM_UNDEFINED)); + } + + struct wrapper_data wd; + wd.func = scmFunc; + wd.args = args_list; + scm_internal_catch (SCM_BOOL_T, + (scm_t_catch_body) lazy_apply_wrapper, (void *) &wd, + (scm_t_catch_handler) empty_handler, 0); +} +#endif