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