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