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