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