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