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