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