Split user list from Channel into ChannelUserList
[clinton/bobotpp.git] / source / Parser.C
CommitLineData
cb21075d 1// Parser.C -*- C++ -*-
2// Copyright (c) 1997, 1998 Etienne BERNARD
55f2215d 3// Copyright (C) 2002,2003,2005,2008 Clinton Ebadi
cb21075d 4
5// This program is free software; you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation; either version 2 of the License, or
8// any later version.
9
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14
15// You should have received a copy of the GNU General Public License
16// along with this program; if not, write to the Free Software
7b564711 17// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18// 02110-1301, USA.
cb21075d 19
20#ifdef HAVE_CONFIG_H
21#include "config.h"
22#endif
23
cfa82921 24#include "Parser.H"
25
55f2215d 26#include <cstdlib>
cfa82921 27
cb21075d 28#include <sys/types.h>
29#include <netinet/in.h>
30
cfa82921 31#ifdef USESCRIPTS
32#include <libguile.h>
33#include "BotInterp.H"
34#include "Interp.H"
35#endif
36
37#include "Bot.H"
38#include "Channel.H"
39#include "ChannelList.H"
4679dc8b 40#include "Commands.H"
cb21075d 41#include "Macros.H"
cfa82921 42#include "Message.H"
43#include "Person.H"
44#include "Server.H"
45#include "ServerConnection.H"
46#include "ServerList.H"
47#include "ShitEntry.H"
cb21075d 48#include "ShitList.H"
cfa82921 49#include "StringTokenizer.H"
50#include "UserCommands.H"
51#include "User.H"
52#include "UserList.H"
53#include "Utils.H"
54
cb21075d 55
e07b6b46 56typedef void (*fptr) (ServerConnection *, Person *, String);
ae97d6ec 57std::map < std::string, fptr, std::less < std::string > > Parser::functions;
439869bf 58
e07b6b46 59void
60Parser::init ()
439869bf 61{
e07b6b46 62 // Parser functions
63 Parser::functions["001"] = Parser::parse001; /* RPL_WELCOME */
64 Parser::functions["302"] = Parser::parse302; /* RPL_USERHOST */
65 Parser::functions["311"] = Parser::parse311; /* RPL_WHOISUSER */
66 Parser::functions["315"] = Parser::parse315; /* RPL_ENDOFWHO */
67 Parser::functions["324"] = Parser::parse324; /* RPL_CHANNELMODEIS */
68 Parser::functions["332"] = Parser::parse332; /* RPL_TOPIC */
69 Parser::functions["352"] = Parser::parse352; /* RPL_WHOREPLY */
70 Parser::functions["353"] = Parser::parse353; /* RPL_NAMESREPLY */
71 Parser::functions["366"] = Parser::parse366; /* RPL_ENDOFNAMES */
72 Parser::functions["367"] = Parser::parse367; /* RPL_BANLIST */
73 Parser::functions["401"] = Parser::parse401; /* ERR_NOSUCHNICK */
74 Parser::functions["433"] = Parser::parse433; /* ERR_NICKNAMEINUSE */
75 Parser::functions["437"] = Parser::parse433; /* ERR_UNAVAILRESOURCE */
76 Parser::functions["471"] = Parser::parse473; /* ERR_CHANNELISFULL */
77 Parser::functions["473"] = Parser::parse473; /* ERR_INVITEONLYCHAN */
78 Parser::functions["474"] = Parser::parse473; /* ERR_BANNEDFROMCHAN */
79 Parser::functions["475"] = Parser::parse473; /* ERR_BADCHANNELKEY */
439869bf 80 Parser::functions["ERROR"] = Parser::parseError;
81 Parser::functions["INVITE"] = Parser::parseInvite;
82 Parser::functions["JOIN"] = Parser::parseJoin;
e07b6b46 83 Parser::functions["KICK"] = Parser::parseKick;
84 Parser::functions["MODE"] = Parser::parseMode;
85 Parser::functions["NICK"] = Parser::parseNick;
439869bf 86 Parser::functions["NOTICE"] = Parser::parseNotice;
87 Parser::functions["PART"] = Parser::parsePart;
88 Parser::functions["PING"] = Parser::parsePing;
89 Parser::functions["PONG"] = Parser::parsePong;
90 Parser::functions["PRIVMSG"] = Parser::parsePrivmsg;
91 Parser::functions["QUIT"] = Parser::parseQuit;
92 Parser::functions["TOPIC"] = Parser::parseTopic;
93 Parser::functions[""] = Parser::parseError;
94}
cb21075d 95
96
97void
e07b6b46 98Parser::parseLine (ServerConnection * cnx, String line)
cb21075d 99{
e07b6b46 100 StringTokenizer st (line);
101 Person *from = 0;
cb21075d 102#ifdef USESCRIPTS
e07b6b46 103 cnx->bot->botInterp->RunHooks (Hook::RAW, line,
46af1667 104 scm_list_n (Utils::
a6339323 105 str2scm (line),
e07b6b46 106 SCM_UNDEFINED));
cb21075d 107#endif
e07b6b46 108 if (line[0] == ':')
109 {
a6339323 110 String fromMask = st.next_token ().substr (1);
e07b6b46 111 if (fromMask.find ('!') != -1)
112 from = new Person (cnx->bot, fromMask);
113 }
cb21075d 114
a6339323 115 String command = st.next_token ();
e07b6b46 116 String rest = st.rest ();
fed59248 117
86e67477 118 // We must use map<>::find or else a new entry will be created in
119 // the map which will cause another lookup of the same invalid
120 // command to segfault the bot
121 std::map<std::string, fptr, std::less<std::string> >::const_iterator cit
122 = functions.find (command);
123
124 if (cit != functions.end ())
125 {
126 fptr temp_func = cit->second;
127 temp_func (cnx, from, rest);
128 }
129
cb21075d 130 delete from;
131}
132
133void
e07b6b46 134Parser::parse001 (ServerConnection * cnx, Person * from, String rest)
cb21075d 135{
136 String temp = "";
e07b6b46 137 StringTokenizer st (rest);
a6339323 138 String realNick = st.next_token ();
0316e2c1 139 if ((cnx->bot->nickName).toLower () != realNick.toLower ())
e07b6b46 140 {
141 // Yes, this can happen, and it was a very subtle bug
142 cnx->bot->nickName = realNick;
143 cnx->bot->userList->removeFirst ();
144 cnx->bot->userList->addUserFirst (realNick + "!" +
145 cnx->bot->userHost, "*", 0,
146 3, true, -1, "");
147 cnx->bot->lastNickNameChange = time (0);
148 cnx->bot->rehash ();
149 }
cb21075d 150
151 cnx->bot->connected = true;
e07b6b46 152 cnx->queue->sendUserMode (cnx->bot->nickName, "+i");
153 cnx->queue->sendWhois (cnx->bot->nickName);
154 for (std::map < String, wantedChannel *,
155 std::less < String > >::iterator it =
156 cnx->bot->wantedChannels.begin ();
157 it != cnx->bot->wantedChannels.end (); ++it)
158 cnx->queue->sendJoin ((*it).first, (*it).second->key);
159 cnx->bot->logLine (String ("Connected to server ") +
160 cnx->bot->serverList->currentServer ()->
161 getHostName () + " (" +
162 String ((long) cnx->bot->serverList->
163 currentServer ()->getPort ()) + ").");
cb21075d 164}
165
166void
e07b6b46 167Parser::parse302 (ServerConnection * cnx, Person * from, String rest)
cb21075d 168{
169 unsigned long num = cnx->bot->receivedUserhostID++;
e07b6b46 170 StringTokenizer st (rest);
a6339323 171 st.next_token (':');
e07b6b46 172 if (st.rest ().length ())
173 {
a6339323 174 st.next_token ('=');
f9723c92 175 cnx->bot->userhostMap[num] = Utils::trim_str (st.rest().substr(1));
e07b6b46 176 }
177 else
cb21075d 178 cnx->bot->userhostMap[num] = "";
179}
180
181void
e07b6b46 182Parser::parse311 (ServerConnection * cnx, Person * from, String rest)
cb21075d 183{
e07b6b46 184 StringTokenizer st (rest);
a6339323 185 st.next_token ();
186 String nuh = st.next_token () + "!";
187 String uh = st.next_token () + "@";
188 uh = uh + st.next_token ();
cb21075d 189 nuh = nuh + uh;
e07b6b46 190 cnx->bot->userList->addUserFirst (nuh, "*", 0, 3, true, -1, "");
cb21075d 191 cnx->bot->userHost = uh;
192}
193
194void
e07b6b46 195Parser::parse315 (ServerConnection * cnx, Person * from, String rest)
cb21075d 196{
e07b6b46 197 StringTokenizer st (rest);
a6339323 198 st.next_token ();
199 String channel = st.next_token ();
e07b6b46 200 Channel *c = cnx->bot->channelList->getChannel (channel);
cb21075d 201 if (!c)
202 return;
203 c->gotWho = true;
204}
205
206void
e07b6b46 207Parser::parse324 (ServerConnection * cnx, Person * from, String rest)
cb21075d 208{
e07b6b46 209 StringTokenizer st (rest);
a6339323 210 st.next_token ();
211 String channel = st.next_token ();
e07b6b46 212 if (Channel * c = cnx->bot->channelList->getChannel (channel))
213 if (c)
214 c->parseMode (from, st.rest ());
cb21075d 215}
216
217void
e07b6b46 218Parser::parse332 (ServerConnection * cnx, Person * from, String rest)
cb21075d 219{
e07b6b46 220 StringTokenizer st (rest);
a6339323 221 st.next_token ();
222 String channel = st.next_token ();
e07b6b46 223 if (Channel * c = cnx->bot->channelList->getChannel (channel))
224 if (c)
a6339323 225 c->channelTopic = st.rest ().substr (1);
cb21075d 226}
227
228void
e07b6b46 229Parser::parse352 (ServerConnection * cnx, Person * from, String rest)
cb21075d 230{
e07b6b46 231 StringTokenizer st (rest);
a6339323 232 st.next_token ();
233 String ch = st.next_token ();
234 String uh = st.next_token () + "@";
235 uh = uh + st.next_token ();
236 st.next_token ();
237 String n = st.next_token ();
238 String m = st.next_token ();
cb21075d 239 int mode = 0;
e07b6b46 240 for (int i = 0; i < m.length (); i++)
241 switch (m[i])
242 {
243 case 'H':
244 break;
245 case 'G':
246 mode |= User::AWAY_MODE;
247 break;
248 case '*':
249 mode |= User::IRCOP_MODE;
250 break;
251 case '@':
252 mode |= User::OP_MODE;
253 break;
254 case '+':
255 mode |= User::VOICE_MODE;
256 break;
257 }
258 if (Channel * c = cnx->bot->channelList->getChannel (ch))
259 if (c)
260 c->addNick (n, uh, mode, cnx->bot->userList);
cb21075d 261}
262
263void
e07b6b46 264Parser::parse353 (ServerConnection * cnx, Person * from, String rest)
cb21075d 265{
266 int mode = 0;
267 String nick;
e07b6b46 268 StringTokenizer st (rest);
a6339323 269 st.next_token ();
270 st.next_token ();
271 Channel *c = cnx->bot->channelList->getChannel (st.next_token ());
e07b6b46 272 if (!c)
273 return;
a6339323 274 StringTokenizer st2 (st.next_token (':'));
275 while (st2.more_tokens_p ())
e07b6b46 276 {
a6339323 277 nick = st2.next_token ();
e07b6b46 278 if (nick[0] == '@')
279 {
280 mode = User::OP_MODE;
a6339323 281 nick = nick.substr (1);
e07b6b46 282 }
283 else if (nick[0] == '+')
284 {
285 mode = User::VOICE_MODE;
a6339323 286 nick = nick.substr (1);
e07b6b46 287 }
288 c->addNick (nick, "", mode, 0, true);
cb21075d 289 }
cb21075d 290}
291
292void
e07b6b46 293Parser::parse366 (ServerConnection * cnx, Person * from, String rest)
cb21075d 294{
e07b6b46 295 StringTokenizer st (rest);
a6339323 296 st.next_token ();
297 String ch = st.next_token ();
e07b6b46 298 if (Channel * c = cnx->bot->channelList->getChannel (ch))
cb21075d 299 c->joined = true;
300}
301
302void
e07b6b46 303Parser::parse367 (ServerConnection * cnx, Person * from, String rest)
cb21075d 304{
e07b6b46 305 StringTokenizer st (rest);
a6339323 306 st.next_token ();
307 String ch = st.next_token ();
e07b6b46 308 if (Channel * c = cnx->bot->channelList->getChannel (ch))
f9723c92 309 c->addBan (Mask(st.next_token ()), -1);
cb21075d 310}
311
312void
e07b6b46 313Parser::parse401 (ServerConnection * cnx, Person * from, String rest)
cb21075d 314{
e07b6b46 315 StringTokenizer st (rest);
a6339323 316 st.next_token ();
317 String nick = st.next_token ();
e07b6b46 318 if (cnx->bot->spyList.find (nick) != cnx->bot->spyList.end ())
319 {
320 delete cnx->bot->spyList[nick];
321 cnx->bot->spyList.erase (nick);
322 }
cb21075d 323}
324
325void
e07b6b46 326Parser::parse433 (ServerConnection * cnx, Person * from, String rest)
cb21075d 327{
328 if (cnx->bot->connected)
329 return;
e07b6b46 330 if (cnx->bot->nickName.length () == 9)
331 {
332 int i;
333 for (i = 0;
334 i < cnx->bot->nickName.length ()
335 && cnx->bot->nickName[i] == '_'; i++);
336 if (i < cnx->bot->nickName.length ())
337 cnx->bot->nickName =
a6339323 338 cnx->bot->nickName.substr (0,
e07b6b46 339 i - 1) + "_" +
a6339323 340 cnx->bot->nickName.substr (i + 1);
e07b6b46 341 else
a6339323 342 cnx->bot->nickName = cnx->bot->nickName.substr (0, 4) +
e07b6b46 343 String ((long) (rand () % 10000));
344 }
345 else
346 cnx->bot->nickName = cnx->bot->nickName + "_";
347 cnx->queue->sendNick (cnx->bot->nickName);
cb21075d 348}
349
350void
e07b6b46 351Parser::parse473 (ServerConnection * cnx, Person * from, String rest)
cb21075d 352{
e07b6b46 353 StringTokenizer st (rest);
a6339323 354 st.next_token ();
e07b6b46 355 cnx->bot->logLine (String ("Unable to join channel ") +
a6339323 356 st.next_token () + ".");
cb21075d 357}
358
359void
e07b6b46 360Parser::parseError (ServerConnection * cnx, Person * from, String rest)
cb21075d 361{
e07b6b46 362 cnx->bot->logLine (String ("Error from server ") +
363 cnx->bot->serverList->currentServer ()->
364 getHostName () + " (" +
365 String ((long) cnx->bot->serverList->
366 currentServer ()->getPort ()) + ").");
367 cnx->bot->nextServer ();
cb21075d 368}
369
370void
e07b6b46 371Parser::parseInvite (ServerConnection * cnx, Person * from, String rest)
cb21075d 372{
e07b6b46 373 String nick = from->getNick ();
374 StringTokenizer st (rest);
a6339323 375 st.next_token (':');
e07b6b46 376 String channel = st.rest ();
cb21075d 377#ifdef USESCRIPTS
e07b6b46 378 cnx->bot->botInterp->RunHooks (Hook::INVITE,
379 nick + " " + channel,
46af1667 380 scm_list_n (Utils::
a6339323 381 str2scm (nick),
e07b6b46 382 Utils::
a6339323 383 str2scm
e07b6b46 384 (channel), SCM_UNDEFINED));
cb21075d 385#endif
e07b6b46 386 if (cnx->bot->wantedChannels.find (channel) !=
387 cnx->bot->wantedChannels.end ())
388 cnx->queue->sendJoin (channel, cnx->bot->wantedChannels[channel]->key);
cb21075d 389}
390
391void
e07b6b46 392Parser::parseJoin (ServerConnection * cnx, Person * from, String rest)
cb21075d 393{
e07b6b46 394 StringTokenizer st (from->getAddress ());
a6339323 395 String n = st.next_token ('!');
396 String uh = st.next_token ();
e07b6b46 397 StringTokenizer st2 (rest);
a6339323 398 String c = st2.next_token (':');
cb21075d 399 String mode;
400 bool joinAndMode = false;
cb21075d 401#ifdef USESCRIPTS
e07b6b46 402 cnx->bot->botInterp->RunHooks (Hook::JOIN, n + " " + c,
46af1667 403 scm_list_n (Utils::
a6339323 404 str2scm (n),
e07b6b46 405 Utils::
a6339323 406 str2scm (c), SCM_UNDEFINED));
cb21075d 407#endif
cb21075d 408 // This part of code is for the combined JOIN & MODE of ircd 2.9
e07b6b46 409 if (c.find ('\007') >= 0)
410 {
411 joinAndMode = true;
412 StringTokenizer st3 (c);
a6339323 413 c = st3.next_token ('\007');
e07b6b46 414 String m = st3.rest ();
415 mode = c + " +" + m;
416 for (int i = 0; i < m.length (); i++)
417 mode = mode + " " + n;
cb21075d 418 }
e07b6b46 419
420 if (n == cnx->bot->nickName)
421 {
422 cnx->bot->logLine (String ("Joined channel ") + c + ".");
423 if (cnx->bot->wantedChannels.find (c) !=
424 cnx->bot->wantedChannels.end ())
425 cnx->bot->channelList->
426 addChannel (cnx, c, cnx->bot->wantedChannels[c]->keep);
427 else
428 cnx->bot->channelList->addChannel (cnx, c);
429 cnx->queue->sendWho (c);
430 cnx->queue->sendChannelMode (String ("MODE ") + c + " b");
431 cnx->queue->sendChannelMode (String ("MODE ") + c);
432 }
433 else
434 {
435 Channel *ch = cnx->bot->channelList->getChannel (c);
436 if (!ch)
437 return;
438 ShitEntry *se = cnx->bot->shitList->getShit (n + "!" + uh, c);
439 if (se && se->isStillValid () &&
440 se->getShitLevel () >= ShitEntry::SHIT_NOJOIN)
441 {
442 cnx->queue->sendChannelMode (c, "+b", se->getMask ());
443 cnx->queue->sendKick (c, n, se->getShitReason ());
444 return;
445 }
446 ch->addNick (n, uh, 0, cnx->bot->userList);
71cf2688 447 if (ch->getUser (n).getAop ()
448 && !(ch->getUser (n).mode & User::OP_MODE) && cnx->bot->iAmOp (c))
e07b6b46 449 {
450 // This is a part of the antispoof code
71cf2688 451 ch->change_user_key (n, Utils::get_key ());
672b7d4e 452 Commands::CTCP (cnx->bot, n, "PING",
71cf2688 453 ch->getUser(n).userkey + " " + c);
e07b6b46 454 }
cb21075d 455 }
cb21075d 456
457 if (joinAndMode)
e07b6b46 458 parseMode (cnx, 0, mode);
cb21075d 459}
460
461void
e07b6b46 462Parser::parseKick (ServerConnection * cnx, Person * from, String rest)
cb21075d 463{
e07b6b46 464 StringTokenizer st (rest);
a6339323 465 String channel = st.next_token ();
466 String target = st.next_token ();
467 String reason = st.rest ().substr (1);
cb21075d 468#ifdef USESCRIPTS
e07b6b46 469 cnx->bot->botInterp->RunHooks (Hook::KICK,
470 target + " " +
471 from->getNick () + " " +
472 channel + " " + reason,
46af1667 473 scm_list_n (Utils::
a6339323 474 str2scm
e07b6b46 475 (target),
476 Utils::
a6339323 477 str2scm (from->
e07b6b46 478 getNick
479 ()),
480 Utils::
a6339323 481 str2scm
e07b6b46 482 (channel),
483 Utils::
a6339323 484 str2scm
e07b6b46 485 (reason), SCM_UNDEFINED));
cb21075d 486#endif
e07b6b46 487 if (target == cnx->bot->nickName)
488 {
489 cnx->bot->logLine (from->getAddress () +
490 " kicked me out of channel " + channel +
491 " (" + reason + ").");
492 cnx->queue->sendJoin (channel,
493 cnx->bot->channelList->
494 getChannel (channel)->channelKey);
495 cnx->bot->channelList->delChannel (channel);
496 }
497 else
498 {
499 if (!cnx->bot->channelList->getChannel (channel))
500 return;
71cf2688 501 try
e07b6b46 502 {
71cf2688 503 User u = cnx->bot->channelList->getChannel (channel)->getUser (target);
504 if (u.getProt () >= User::NO_KICK)
e07b6b46 505 {
71cf2688 506 String fromNick = from->getNick ();
507 User v =
508 cnx->bot->channelList->getChannel (channel)->getUser (fromNick);
509 if (v.getProt () < User::NO_KICK)
510 {
511 cnx->bot->logLine (from->getAddress () + " kicked " + target +
512 " (protected) out of channel " + channel +
513 " (" + reason + ").");
514 cnx->queue->sendKick (channel, fromNick,
515 target + " \002is protected !\002");
516 }
e07b6b46 517 }
518 }
71cf2688 519 catch (const ChannelUserList::user_not_found &e)
520 {
521
522 }
e07b6b46 523 cnx->bot->channelList->getChannel (channel)->delNick (target);
cb21075d 524 }
cb21075d 525}
526
527void
e07b6b46 528Parser::parseMode (ServerConnection * cnx, Person * from, String rest)
cb21075d 529{
e07b6b46 530 StringTokenizer st (rest);
a6339323 531 String ch = st.next_token ();
e07b6b46 532 String modes = st.rest ();
cb21075d 533#ifdef USESCRIPTS
e07b6b46 534 if (from)
535 cnx->bot->botInterp->RunHooks (Hook::MODE,
536 from->getNick () + " " + ch +
537 " " + modes,
46af1667 538 scm_list_n (Utils::
a6339323 539 str2scm (from->
e07b6b46 540 getNick
541 ()),
542 Utils::
a6339323 543 str2scm (ch),
e07b6b46 544 Utils::
a6339323 545 str2scm (modes),
e07b6b46 546 SCM_UNDEFINED));
cb21075d 547#endif
a6339323 548 if (Utils::channel_p (ch))
e07b6b46 549 {
550 Channel *c = cnx->bot->channelList->getChannel (ch);
551 if (!c)
552 return;
553 if (from)
554 c->parseMode (from, modes);
555 else
556 c->parseMode (0, modes);
557 }
cb21075d 558}
559
560void
e07b6b46 561Parser::parseNick (ServerConnection * cnx, Person * from, String rest)
cb21075d 562{
e07b6b46 563 String on_orig = from->getNick ();
564 String on = on_orig.toLower ();
a6339323 565 String nn = rest.substr (1);
e07b6b46 566 String nn_lower = nn.toLower ();
cb21075d 567#ifdef USESCRIPTS
e07b6b46 568 cnx->bot->botInterp->RunHooks (Hook::NICKNAME,
569 on_orig + " " + nn,
46af1667 570 scm_list_n (Utils::
a6339323 571 str2scm
e07b6b46 572 (on_orig),
573 Utils::
a6339323 574 str2scm (nn),
e07b6b46 575 SCM_UNDEFINED));
cb21075d 576#endif
e07b6b46 577 if ((cnx->bot->nickName).toLower () == on)
578 {
579 cnx->bot->userList->removeFirst ();
580 cnx->bot->userList->addUserFirst (nn + "!" +
581 cnx->bot->userHost, "*", 0,
582 3, true, -1, "");
583 cnx->bot->lastNickNameChange = time (0);
584 cnx->bot->nickName = nn;
585 cnx->bot->rehash ();
586 }
cb21075d 587
e07b6b46 588 if (cnx->bot->spyList.find (on) != cnx->bot->spyList.end ())
589 {
590 cnx->bot->spyList[nn_lower] = cnx->bot->spyList[on];
591 cnx->bot->spyList.erase (on);
592 }
593
594 for (std::map < String, Channel *,
595 std::less < String > >::iterator it =
596 cnx->bot->channelList->begin ();
597 it != cnx->bot->channelList->end (); ++it)
598 if ((*it).second->hasNick (on))
599 (*it).second->changeNick (on, nn_lower);
cb21075d 600}
601
602void
e07b6b46 603Parser::parseNotice (ServerConnection * cnx, Person * from, String rest)
cb21075d 604{
605 String nick = "";
cb21075d 606 if (from)
e07b6b46 607 nick = from->getNick ();
608 StringTokenizer st (rest);
a6339323 609 String to = st.next_token ();
610 rest = st.rest ().substr (1);
e07b6b46 611 if (rest[0] != '\001')
612 {
cb21075d 613#ifdef USESCRIPTS
a6339323 614 if (Utils::channel_p (to))
e07b6b46 615 cnx->bot->botInterp->RunHooks (Hook::PUBLIC_NOTICE,
616 nick + " " + to + " " + rest,
46af1667 617 scm_list_n (Utils::
a6339323 618 str2scm (nick),
e07b6b46 619 Utils::
a6339323 620 str2scm (to),
e07b6b46 621 Utils::
a6339323 622 str2scm (rest),
e07b6b46 623 SCM_UNDEFINED));
624 else
625 cnx->bot->botInterp->RunHooks (Hook::NOTICE, nick + " " + rest,
46af1667 626 scm_list_n (Utils::
a6339323 627 str2scm (nick),
e07b6b46 628 Utils::
a6339323 629 str2scm (rest),
e07b6b46 630 SCM_UNDEFINED));
cb21075d 631#endif
e07b6b46 632 return;
633 }
cb21075d 634
a6339323 635 rest = rest.substr (1, rest.length () - 2);
e07b6b46 636 StringTokenizer st2 (rest);
a6339323 637 String command = st2.next_token ();
e07b6b46 638 rest = st2.rest ();
cb21075d 639#ifdef USESCRIPTS
e07b6b46 640 cnx->bot->botInterp->RunHooks (Hook::CTCP_REPLY,
641 nick + " " + command + " " +
642 rest,
46af1667 643 scm_list_n (Utils::
a6339323 644 str2scm (nick),
e07b6b46 645 Utils::
a6339323 646 str2scm
e07b6b46 647 (command),
648 Utils::
a6339323 649 str2scm (rest),
e07b6b46 650 SCM_UNDEFINED));
cb21075d 651#endif
e07b6b46 652 if (command == "PING")
653 {
654 StringTokenizer st3 (rest);
a6339323 655 rest = st3.next_token ();
e07b6b46 656 String c = st3.rest ();
71cf2688 657 Channel *channel = cnx->bot->channelList->getChannel (c);
658
659 if (channel && channel->hasNick (nick))
660 {
661 User u = cnx->bot->channelList->getChannel(c)->getUser(nick);
662 if (u.getAop () && !(u.mode & User::OP_MODE) && u.userkey == rest)
663 cnx->queue->sendChannelMode (c, "+o", nick);
664 }
e07b6b46 665 }
cb21075d 666}
667
668void
e07b6b46 669Parser::parsePrivmsg (ServerConnection * cnx, Person * from, String rest)
cb21075d 670{
e07b6b46 671 String nick = from->getNick ();
672 StringTokenizer st (rest);
a6339323 673 String to = st.next_token ();
674 String fromUserhost = Utils::get_userhost (from->getAddress ());
675 rest = st.rest ().substr (1);
e07b6b46 676 if (++(cnx->bot->ignoredUserhosts[fromUserhost]) > Bot::MAX_MESSAGES)
677 {
678 if (cnx->bot->ignoredUserhosts[fromUserhost] == Bot::MAX_MESSAGES + 1)
679 {
cb21075d 680#ifdef USESCRIPTS
e07b6b46 681 cnx->bot->botInterp->RunHooks (Hook::FLOOD, nick,
46af1667 682 scm_list_n (Utils::
a6339323 683 str2scm (nick),
e07b6b46 684 SCM_UNDEFINED));
cb21075d 685#endif
e07b6b46 686 cnx->bot->ignoredUserhosts[fromUserhost] += Bot::IGNORE_DELAY;
687 cnx->bot->logLine (from->getAddress () +
688 " is flooding me. We will ignore him/her/it.");
a6339323 689 if (!Utils::channel_p (to))
e07b6b46 690 from->
691 sendNotice (String ("\002You are now being ignored for ") +
692 String ((long) Bot::IGNORE_DELAY) +
693 " seconds.\002");
694 }
695 // The following lines reset the counter if you use the
696 // command "!sorry" (if '!' is your command char).
697 // This is not documented, I know. But one probably does
698 // not want that every users can bypass the flood control
699 // Of course, if you want this feature to remain 'secret',
700 // do not use it in public.
701 if (rest.toUpper () == String (cnx->bot->commandChar) + "SORRY")
702 {
703 cnx->bot->ignoredUserhosts[fromUserhost] = 0;
704 from->sendNotice ("\002Don't do it again!\002");
705 }
706 return;
cb21075d 707 }
e07b6b46 708
709 if (rest[0] == '\001')
710 {
a6339323 711 rest = rest.substr (1, rest.length () - 2);
712 if (!Utils::channel_p (to))
e07b6b46 713 for (std::map < String, Person *,
714 std::less < String > >::iterator it =
715 cnx->bot->spyList.begin (); it != cnx->bot->spyList.end (); ++it)
716 (*it).second->sendNotice (String ("CTCP From ") +
717 nick + ": " + rest);
718 Parser::parseCTCP (cnx, from, to, rest);
719 }
720 else
721 {
722 if ((rest.length () < 5 ||
a6339323 723 rest.substr (1, 5).toUpper () != "IDENT") &&
e07b6b46 724 (rest.length () < 8 ||
a6339323 725 rest.substr (1, 8).toUpper () != "PASSWORD") &&
726 !Utils::channel_p (to))
e07b6b46 727 for (std::map < String, Person *,
728 std::less < String > >::iterator it =
729 cnx->bot->spyList.begin (); it != cnx->bot->spyList.end (); ++it)
730 (*it).second->sendNotice (String ("*") + nick + "* " + rest);
731 Parser::parseMessage (cnx, from, to, rest);
cb21075d 732 }
cb21075d 733}
734
735void
e07b6b46 736Parser::parsePart (ServerConnection * cnx, Person * from, String rest)
cb21075d 737{
e07b6b46 738 String n = from->getNick ();
739 StringTokenizer st (rest);
a6339323 740 String channel = st.next_token ();
cb21075d 741#ifdef USESCRIPTS
ae97d6ec 742 cnx->bot->botInterp->RunHooks (Hook::PART, n + " " + channel,
46af1667 743 scm_list_n (Utils::
a6339323 744 str2scm (n),
e07b6b46 745 Utils::
a6339323 746 str2scm
e07b6b46 747 (channel), SCM_UNDEFINED));
cb21075d 748#endif
e07b6b46 749 if (n.toLower () == cnx->bot->nickName.toLower ())
750 {
ae97d6ec 751 cnx->bot->logLine (String ("Left channel ") + channel + ".");
e07b6b46 752 cnx->bot->channelList->delChannel (channel);
753 }
754 else
755 {
756 Channel *c = cnx->bot->channelList->getChannel (channel);
757 if (!c)
758 return;
759 c->delNick (n);
71cf2688 760 if (c->operator_count () == 0 && c->user_count () == 1)
e07b6b46 761 {
762 cnx->queue->sendPart (channel);
763 cnx->queue->sendJoin (channel,
764 cnx->bot->wantedChannels[channel]->key);
765 }
cb21075d 766 }
cb21075d 767}
768
769void
e07b6b46 770Parser::parsePing (ServerConnection * cnx, Person * from, String rest)
cb21075d 771{
e07b6b46 772 cnx->queue->sendPong (rest);
cb21075d 773}
774
775void
e07b6b46 776Parser::parsePong (ServerConnection * cnx, Person * from, String rest)
cb21075d 777{
e07b6b46 778 cnx->lag = (cnx->lag + 2 * (time (NULL) - cnx->pingTime)) / 3;
cb21075d 779 cnx->bot->sentPing = false;
780}
781
782void
e07b6b46 783Parser::parseQuit (ServerConnection * cnx, Person * from, String rest)
cb21075d 784{
e07b6b46 785 String n = from->getNick ();
cb21075d 786#ifdef USESCRIPTS
e07b6b46 787 cnx->bot->botInterp->RunHooks (Hook::SIGNOFF, n + " " + rest,
46af1667 788 scm_list_n (Utils::
a6339323 789 str2scm (n),
e07b6b46 790 Utils::
a6339323 791 str2scm (rest),
e07b6b46 792 SCM_UNDEFINED));
cb21075d 793#endif
cb21075d 794 if (n == cnx->bot->nickName)
795 cnx->bot->stop = true;
e07b6b46 796 for (std::map < String, Channel *,
797 std::less < String > >::iterator it =
798 cnx->bot->channelList->begin ();
799 it != cnx->bot->channelList->end (); ++it)
800 (*it).second->delNick (n);
cb21075d 801}
802
803void
e07b6b46 804Parser::parseTopic (ServerConnection * cnx, Person * from, String rest)
cb21075d 805{
e07b6b46 806 StringTokenizer st (rest);
a6339323 807 String channel = st.next_token ();
808 String newTopic = st.rest ().substr (1);
e07b6b46 809 Channel *c = cnx->bot->channelList->getChannel (channel);
cb21075d 810#ifdef USESCRIPTS
e07b6b46 811 cnx->bot->botInterp->RunHooks (Hook::TOPIC,
812 from->getNick () + " " +
813 channel + " " + newTopic,
46af1667 814 scm_list_n (Utils::
a6339323 815 str2scm (from->
e07b6b46 816 getNick
817 ()),
818 Utils::
a6339323 819 str2scm
e07b6b46 820 (channel),
821 Utils::
a6339323 822 str2scm
e07b6b46 823 (newTopic), SCM_UNDEFINED));
cb21075d 824#endif
e07b6b46 825 if (!c)
826 return;
827 if (c->lockedTopic && from->getNick () != cnx->bot->nickName)
828 cnx->queue->sendTopic (channel, c->channelTopic);
cb21075d 829 c->channelTopic = newTopic;
830}
831
832void
e07b6b46 833Parser::parseCTCP (ServerConnection * cnx,
834 Person * from, String to, String parameters)
cb21075d 835{
e07b6b46 836 StringTokenizer st (parameters);
a6339323 837 String command = Utils::to_upper (st.next_token ());
e07b6b46 838 String nick = from->getNick ();
cb21075d 839 String rest;
a6339323 840 if (st.more_tokens_p ())
e07b6b46 841 rest = st.rest ();
cb21075d 842 else
843 rest = "";
cb21075d 844#ifdef USESCRIPTS
e07b6b46 845 cnx->bot->botInterp->RunHooks (Hook::CTCP,
846 nick + " " + to + " " +
847 command + " " + rest,
46af1667 848 scm_list_n (Utils::
a6339323 849 str2scm (nick),
e07b6b46 850 Utils::
a6339323 851 str2scm (to),
e07b6b46 852 Utils::
a6339323 853 str2scm
e07b6b46 854 (command),
855 Utils::
a6339323 856 str2scm (rest),
e07b6b46 857 SCM_UNDEFINED));
cb21075d 858#endif
cb21075d 859 if (command == "PING")
672b7d4e 860 {
861 Commands::CTCPReply (cnx->bot, nick, "PING", rest);
862 }
cb21075d 863 else if (command == "VERSION")
6b7614a8 864 {
672b7d4e 865 Commands::CTCPReply (cnx->bot, nick, "VERSION",
866 cnx->bot->versionString);
6b7614a8 867 }
e07b6b46 868 else if (command == "CLOCK")
869 {
870 time_t diff = time (NULL) - cnx->bot->startTime;
672b7d4e 871 Commands::CTCPReply (cnx->bot, nick, "CLOCK",
872 String ("elapsed time: ") +
873 String ((long) (diff / 86400)) +
874 "d" +
875 String ((long) (diff % 86400) /
876 3600) + "h" +
877 String ((long) (diff % 3600) / 60) +
878 "m" + String ((long) (diff % 60)) + "s");
e07b6b46 879 }
880 else if (command == "COMMAND")
672b7d4e 881 {
882 Commands::CTCPReply (cnx->bot, nick,
883 "COMMAND", String (cnx->bot->commandChar));
884 }
cb21075d 885 else if (command == "LAG")
672b7d4e 886 {
887 Commands::CTCPReply (cnx->bot, nick, "LAG",
888 String ((long) cnx->lag) + " second(s)");
889 }
e07b6b46 890 else if (command == "DCC")
891 {
892 StringTokenizer st2 (rest);
a6339323 893 command = Utils::to_upper (st2.next_token ());
e07b6b46 894 if (command == "CHAT")
895 {
fd7440f1 896 // FIXME: debug DCC
a6339323 897 st2.next_token ();
e07b6b46 898 unsigned long address =
a6339323 899 std::strtoul (st2.next_token ().c_str(), 0, 0);
900 int port = std::atoi (st2.next_token().c_str());
901 if (port >= 1024 && Utils::get_level (cnx->bot, from->getAddress ()))
4edefeb6 902 cnx->bot->addDCC (from, address, port, Bot::CHAT);
fd7440f1 903 else
904 cnx->bot->logLine ("DCC Chat Failed in Parser");
e07b6b46 905 }
cb21075d 906 }
cb21075d 907#ifdef USESCRIPTS
e07b6b46 908 else if (command == "ACTION")
909 {
910 cnx->bot->botInterp->RunHooks (Hook::ACTION,
c7d8cfb9 911 from->getNick () + " " + to +
e07b6b46 912 " " + rest,
46af1667 913 scm_list_n (Utils::
a6339323 914 str2scm (from->
c7d8cfb9 915 getNick
e07b6b46 916 ()),
917 Utils::
a6339323 918 str2scm (to),
e07b6b46 919 Utils::
a6339323 920 str2scm (rest),
e07b6b46 921 SCM_UNDEFINED));
922 }
cb21075d 923#endif
924}
925
cb21075d 926void
e07b6b46 927Parser::parseMessage (ServerConnection * cnx,
928 Person * from, String to, String parameters)
cb21075d 929{
930#ifdef USESCRIPTS
a6339323 931 if (Utils::channel_p (to))
e07b6b46 932 cnx->bot->botInterp->RunHooks (Hook::PUBLIC,
933 from->getNick () + " " + to +
934 " " + parameters,
46af1667 935 scm_list_n (Utils::
a6339323 936 str2scm (from->
e07b6b46 937 getNick
938 ()),
939 Utils::
a6339323 940 str2scm (to),
e07b6b46 941 Utils::
a6339323 942 str2scm
e07b6b46 943 (parameters), SCM_UNDEFINED));
cb21075d 944 else
e07b6b46 945 cnx->bot->botInterp->RunHooks (Hook::MESSAGE,
946 from->getNick () + " " +
947 parameters,
46af1667 948 scm_list_n (Utils::
a6339323 949 str2scm (from->
e07b6b46 950 getNick
951 ()),
952 Utils::
a6339323 953 str2scm
e07b6b46 954 (parameters), SCM_UNDEFINED));
cb21075d 955#endif
cb21075d 956 if (parameters[0] != cnx->bot->commandChar)
957 return;
958
e07b6b46 959 StringTokenizer st (parameters);
a6339323 960 String command = Utils::to_upper (st.next_token ().substr (1));
961 String rest = Utils::trim_str (st.rest ());
cb21075d 962 int level;
963 bool identified = false;
7b1339a2 964 std::map<std::string, class userFunction*,
965 std::less<std::string> >::const_iterator uf_iter
966 = cnx->bot->userFunctions.find (command);
967 userFunction * f = 0;
968
969 if (uf_iter != cnx->bot->userFunctions.end ())
970 f = uf_iter->second;
971 else
972 return;
973
e07b6b46 974 if (f)
975 {
976 if (f->needsChannelName)
977 {
a6339323 978 if (Utils::channel_p (rest))
e07b6b46 979 {
980 StringTokenizer st2 (rest);
a6339323 981 to = st.next_token ();
e07b6b46 982 rest = st.rest ();
983 }
a6339323 984 if (!Utils::channel_p (to))
7b1339a2 985 {
986 from->sendNotice ("\002You need to supply a channel name"
987 " for this command\002");
988 return;
989 }
990 if (!cnx->bot->channelList->getChannel (to))
991 {
992 from->sendNotice (String ("\002I am not on channel\002 ") +
993 to);
994 return;
995 }
a6339323 996 level = Utils::get_level (cnx->bot, from->getAddress (), to);
71cf2688 997
998 Channel * c = cnx->bot->channelList->getChannel (to);
999 if (c && c->hasNick (from->getNick ()))
1000 {
1001 User u = c->getUser (from->getNick ());
1002 if (!u.userListItem)
1003 identified = true;
1004 else
1005 identified = u.userListItem->passwd == ""
1006 || u.userListItem->identified > 0;
1007 }
7b1339a2 1008 else
71cf2688 1009 {
1010 identified = true;
1011 }
7b1339a2 1012 }
1013 else
1014 {
a6339323 1015 level = Utils::get_level (cnx->bot, from->getAddress ());
7b1339a2 1016 identified = true;
1017 }
1018 if (level >= f->minLevel)
1019 {
1020 cnx->bot->logLine (from->getAddress () + " did " + command +
1021 " " + rest);
cb21075d 1022#ifdef USESCRIPTS
7b1339a2 1023 if (f->argsCount != -1)
1024 {
1025 Parser::parseScriptFunction (cnx, to, f->needsChannelName,
1026 f->scmFunc, f->argsCount,
1027 rest);
1028 }
1029 else
1030 {
1031 f->function (cnx, from, to, rest);
1032 }
cb21075d 1033#else
7b1339a2 1034 f->function (cnx, from, to, rest);
cb21075d 1035#endif
7b1339a2 1036 }
1037 else
1038 {
1039 if (!identified)
1040 from->
1041 sendNotice (String
1042 ("\002You are not identified on channel\002 ") +
1043 to);
1044 }
1045 }
cb21075d 1046}
1047
1048#ifdef USESCRIPTS
1049void
e07b6b46 1050Parser::parseScriptFunction (ServerConnection * cnx,
1051 String channel,
1052 bool needsChannelName,
1053 SCM scmFunc, int argsCount, String parameters)
cb21075d 1054{
1055 String param;
46af1667 1056 SCM args_list = scm_list_n (SCM_UNDEFINED);
e07b6b46 1057 if (needsChannelName)
1058 {
46af1667 1059 args_list = scm_append (scm_list_2 (args_list,
1060 scm_list_n (Utils::
1061 str2scm (channel),
1062 SCM_UNDEFINED)));
e07b6b46 1063 argsCount--;
1064 }
cb21075d 1065
e07b6b46 1066 StringTokenizer st (parameters);
1067 for (int i = argsCount; i > 0; i--)
1068 {
1069 if (i == 1)
1070 param = st.rest ();
1071 else
a6339323 1072 param = st.next_token ();
46af1667 1073 args_list = scm_append (scm_list_2 (args_list,
1074 scm_list_n (Utils::str2scm (param),
1075 SCM_UNDEFINED)));
e07b6b46 1076 }
cb21075d 1077
1078 struct wrapper_data wd;
1079 wd.func = scmFunc;
1080 wd.args = args_list;
d56bdd22 1081 scm_internal_catch (SCM_BOOL_T,
ae97d6ec 1082 (scm_t_catch_body) Interp::LazyApplyWrapper,
1083 (void *) &wd,
1084 (scm_t_catch_handler) Interp::EmptyHandler, 0);
cb21075d 1085}
1086#endif