ede6428cd0fe31939cf272f371b548d0939074dc
[clinton/bobotpp.git] / source / Commands.C
1 // Commands.C -*- C++ -*-
2 // Copyright (c) 1998 Etienne BERNARD
3 // Copyright (C) 2002,2005 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 #include "Commands.H"
21
22 #include "BanEntry.H"
23 #include "Bot.H"
24 #include "ChannelList.H"
25 #include "Macros.H"
26 #include "Message.H"
27 #include "Server.H"
28 #include "ServerConnection.H"
29 #include "ServerList.H"
30 #include "ServerQueueItem.H"
31 #include "ShitEntry.H"
32 #include "ShitList.H"
33 #include "StringTokenizer.H"
34 #include "TodoList.H"
35 #include "User.H"
36 #include "UserList.H"
37 #include "Utils.H"
38
39 #ifdef USESCRIPTS
40 #include "BotInterp.H"
41 #endif
42
43
44 #define CHECK_CONNECTION if (!bot->serverConnection) return NotConnected
45
46 Message
47 Commands::Action(Bot *bot, String channel, String message)
48 {
49 CHECK_CONNECTION;
50
51 if (!CHANNEL(channel))
52 return NotOnChannel(channel);
53
54 if (message.length() == 0)
55 return InvalidParameters;
56
57 Commands::CTCP (bot, channel, "ACTION", message);
58
59 return Ok;
60 }
61
62 Message
63 Commands::AddUser(Bot *bot, String who, String maskChannel, int level,
64 int prot, bool aop, std::time_t expire, String password)
65 {
66 // Gah, fix this (makes bot segfault)
67 if (who.length() == 0 ||
68 maskChannel.length() == 0 ||
69 level < 0 ||
70 level > User::FRIEND ||
71 prot < 0 ||
72 prot > User::NO_DEOP)
73 return InvalidParameters;
74
75 String mask;
76
77 if (!Utils::wildcard_p(who))
78 {
79 mask = bot->getUserhost("", who);
80 if (mask.length() == 0)
81 {
82 return NotFound(who);
83 }
84 }
85 // Aha! This was before the brace...segfault gone
86 mask = Utils::make_wildcard(mask);
87
88 if (bot->userList->isInUserList(mask, maskChannel))
89 {
90 return AlreadyInUserlist(mask, maskChannel);
91 }
92
93 bot->userList->addUser(mask, maskChannel, level, prot, aop,
94 expire, password);
95 bot->rehash();
96
97 return Ok;
98 }
99
100
101 Message
102 Commands::AddServer(Bot *bot, String servername, int port)
103 {
104 if (port <= 0)
105 return InvalidPort(port);
106
107 bot->serverList->addServer(new class Server(servername, port));
108
109 return Ok;
110 }
111
112
113 Message
114 Commands::AddShit(Bot *bot, String mask, String maskChannel,
115 int level, time_t expiration, String reason)
116 {
117 if (mask.length() == 0 || maskChannel.length() == 0 ||
118 level < 0 || level > ShitEntry::SHIT_NODEBAN)
119 return InvalidParameters;
120
121 if (reason == "")
122 reason = "You're on my shitlist, lamer";
123
124 String who = mask;
125
126 if (!Utils::wildcard_p(mask)) {
127 mask = bot->getUserhost("", who);
128 if (mask.length() == 0)
129 return NotFound(who);
130 mask = Utils::make_wildcard(mask);
131 if (bot->shitList->getShit(mask, maskChannel))
132 return AlreadyInShitlist(mask, maskChannel);
133 }
134
135 if (bot->userList->getMaxProt(mask, maskChannel) > 0)
136 return UserProtected(who, maskChannel);
137
138 bot->shitList->addShit(mask, maskChannel, level, expiration, reason);
139
140 return Ok;
141 }
142
143
144 Message
145 Commands::Ban(Bot *bot, String channel, String who)
146 {
147 CHECK_CONNECTION;
148
149 Channel *c = CHANNEL(channel);
150
151 if (!c)
152 return NotOnChannel(channel);
153
154 if (!bot->iAmOp(channel))
155 return NotChannelOp(channel);
156
157 String dest;
158
159 if (!Utils::wildcard_p(who))
160 dest = bot->getUserhost(channel, who);
161 else
162 dest = who;
163
164 if (dest.length() == 0)
165 return NotFound(who);
166
167 dest = Utils::make_wildcard(dest);
168 Mask m(dest);
169
170 for (std::list<UserListItem *>::iterator it = bot->userList->l.begin();
171 it != bot->userList->l.end();
172 it++)
173 if (m.matches((*it)->mask) &&
174 (*it)->channelMask.matches(channel) &&
175 (*it)->prot >= User::NO_BAN)
176 return UserProtected(who, channel);
177
178 for (std::vector<BanEntry *>::iterator it = c->channelBanlist.begin();
179 it != c->channelBanlist.end(); ++it)
180 if (m.matches((*it)->getMask ()) && (*it)->getMask().getMask() != m.getMask())
181 QUEUE->sendChannelMode(channel, "-b", (*it)->getMask().getMask());
182
183 QUEUE->sendChannelMode(channel, "+b", dest);
184
185 return Ok;
186 }
187
188 Message
189 Commands::CTCP (Bot *bot, std::string target, std::string command,
190 std::string message)
191 {
192 CHECK_CONNECTION;
193
194 if (target == "")
195 {
196 return EmptyAddressee;
197 }
198
199 if (command == "")
200 {
201 return InvalidParameters;
202 }
203
204 if (message == "")
205 {
206 return EmptyMessage;
207 }
208
209 if (Utils::channel_p (target) && !CHANNEL (target))
210 {
211 return NotOnChannel (target);
212 }
213
214
215 // Send multi-line messages as seperate privmsgs
216 StringTokenizer st_message (message);
217
218 while (st_message.more_tokens_p ('\n'))
219 {
220 QUEUE->sendCTCP (target, command, st_message.next_token ('\n'));
221 }
222
223 return Ok;
224 }
225
226 Message
227 Commands::CTCPReply (Bot *bot, std::string target, std::string command,
228 std::string message)
229 {
230 CHECK_CONNECTION;
231
232 if (target == "")
233 {
234 return EmptyAddressee;
235 }
236
237 if (command == "")
238 {
239 return InvalidParameters;
240 }
241
242 if (message == "")
243 {
244 return EmptyMessage;
245 }
246
247 // CTCP-REPLY cannot go to a channel
248 if (Utils::channel_p (target))
249 {
250 return NotToChannel;
251 }
252
253
254 // Send multi-line messages as seperate privmsgs
255 StringTokenizer st_message (message);
256
257 while (st_message.more_tokens_p ('\n'))
258 {
259 QUEUE->sendCTCPReply (target, command, st_message.next_token ('\n'));
260 }
261
262 return Ok;
263 }
264
265
266 Message
267 Commands::Cycle(Bot *bot, String channel)
268 {
269 CHECK_CONNECTION;
270
271 if (!CHANNEL(channel))
272 return NotOnChannel(channel);
273
274 QUEUE->sendPart(channel);
275 QUEUE->sendJoin(channel, bot->wantedChannels[channel]->key);
276
277 return Ok;
278 }
279
280 Message
281 Commands::Deban(Bot *bot, String channel, String who)
282 {
283 CHECK_CONNECTION;
284
285 Channel *c = CHANNEL(channel);
286
287 if (!c)
288 return NotOnChannel(channel);
289
290 if (!bot->iAmOp(channel))
291 return NotChannelOp(channel);
292
293 String dest;
294
295 if (!Utils::wildcard_p(who))
296 dest = bot->getUserhost(channel, who);
297 else
298 dest = who;
299
300 if (dest.length() == 0)
301 return UserNotFound(who, channel);
302
303 dest = Utils::make_wildcard(dest);
304 Mask m(dest);
305
306 for (std::vector<BanEntry *>::iterator it = c->channelBanlist.begin();
307 it != c->channelBanlist.end(); ++it)
308 if (m.matches((*it)->getMask())) {
309 // Let's see if the ban is in the shitlist
310 ShitEntry *se = bot->shitList->getShit((*it)->getMask().getMask(), channel);
311 if (!se || !se->isStillValid() ||
312 se->getShitLevel() < ShitEntry::SHIT_NODEBAN)
313 QUEUE->sendChannelMode(channel, "-b", (*it)->getMask().getMask());
314 }
315
316 return Ok;
317 }
318
319 Message
320 Commands::DelServer(Bot *bot, int number)
321 {
322 if (number < 0 || number >= bot->serverList->size())
323 return InvalidServerNumber(number);
324
325 bot->serverList->delServer(number);
326
327 return Ok;
328 }
329
330 Message
331 Commands::DelUser(Bot *bot, String who, String maskChannel)
332 {
333 if (who.length() == 0 || maskChannel.length() == 0)
334 return InvalidParameters;
335
336 String dest;
337
338 if (!Utils::wildcard_p(who)) {
339 dest = bot->getUserhost("", who);
340 if (dest.length() == 0)
341 return NotFound(who);
342 dest = Utils::make_wildcard(who);
343 }
344
345 if (!bot->userList->isInUserList(dest, maskChannel))
346 return NotInUserlist(who);
347
348 bot->userList->removeUser(dest, maskChannel);
349 bot->rehash();
350
351 return Ok;
352 }
353
354 Message
355 Commands::DelShit(Bot *bot, String who, String maskChannel)
356 {
357 if (who.length() == 0 || maskChannel.length() == 0)
358 return InvalidParameters;
359
360 String dest;
361
362 if (!Utils::wildcard_p(who)) {
363 dest = bot->getUserhost("", who);
364 if (dest.length() == 0)
365 return NotFound(who);
366 dest = Utils::make_wildcard(who);
367 }
368
369 if (!bot->shitList->getShit(dest, maskChannel))
370 return NotInShitlist(who);
371
372 bot->shitList->delShit(dest, maskChannel);
373
374 return Ok;
375 }
376
377 Message
378 Commands::Deop(Bot *bot, String channel, String who)
379 {
380 CHECK_CONNECTION;
381
382 Channel *c = CHANNEL(channel);
383
384 if (!c)
385 return NotOnChannel(channel);
386
387 if (!bot->iAmOp(channel))
388 return NotChannelOp(channel);
389
390 if (!Utils::wildcard_p(who)) {
391 User *u = c->getUser(who);
392 if (!u)
393 return UserNotFound(who, channel);
394 if (!(u->mode & User::OP_MODE))
395 return UserNotOp(who, channel);
396 if (u->getProt() >= User::NO_DEOP)
397 return UserProtected(who, channel);
398 QUEUE->sendChannelMode(channel, "-o", who);
399 } else {
400 Mask m(who);
401 for (std::map<String, User *, std::less<String> >::iterator
402 it = c->channelMemory.begin();
403 it != c->channelMemory.end(); ++it) {
404 if (m.matches((*it).second->nick + "!" +
405 (*it).second->userhost) &&
406 (*it).second->getProt() < User::NO_DEOP &&
407 ((*it).second->mode & User::OP_MODE))
408 QUEUE->sendChannelMode(channel, "-o", (*it).second->nick);
409 }
410 }
411
412 return Ok;
413 }
414
415 Message
416 Commands::Die(Bot *bot, String reason)
417 {
418 CHECK_CONNECTION;
419
420 QUEUE->sendQuit(reason);
421 bot->stop = true;
422
423 return Ok;
424 }
425
426 Message
427 Commands::Do(Bot *bot, String command)
428 {
429 CHECK_CONNECTION;
430
431 QUEUE->addLine(command, 0, 0, ServerQueueItem::OTHER);
432 return Ok;
433 }
434
435 Message
436 Commands::Invite(Bot *bot, String channel, String who)
437 {
438 CHECK_CONNECTION;
439
440 if (!bot->iAmOp(channel))
441 return NotChannelOp(channel);
442
443 QUEUE->sendInvite(channel, who);
444
445 return Ok;
446 }
447
448 Message
449 Commands::Join(Bot *bot, String channel, String key)
450 {
451 CHECK_CONNECTION;
452
453 if (!Utils::valid_channel_name_p(channel))
454 return InvalidChannel(channel);
455
456 // We change the key only if we are not on the channel.
457 // We don't trust the user...
458 if (!CHANNEL(channel)) {
459 if (bot->wantedChannels[channel])
460 bot->wantedChannels[channel]->key = key;
461 else {
462 bot->wantedChannels[channel] = new wantedChannel("", "", key);
463 }
464 }
465 QUEUE->sendJoin(channel, bot->wantedChannels[channel]->key);
466
467 return Ok;
468 }
469
470 Message
471 Commands::Keep(Bot *bot, String channel, String modes)
472 {
473 CHECK_CONNECTION;
474
475 Channel *c = CHANNEL(channel);
476
477 if (!c)
478 return NotOnChannel(channel);
479
480 c->keepModes = modes;
481
482 return Ok;
483 }
484
485 Message
486 Commands::Kick(Bot *bot, String channel, String who, String reason)
487 {
488 CHECK_CONNECTION;
489
490 Channel *c = CHANNEL(channel);
491
492 if (!c)
493 return NotOnChannel(channel);
494
495 if (!bot->iAmOp(channel))
496 return NotChannelOp(channel);
497
498 if (Utils::wildcard_p(who)) {
499 Mask m(who);
500 for (std::map<String, User *, std::less<String> >::iterator it =
501 c->channelMemory.begin();
502 it != c->channelMemory.end();
503 ++it)
504 if (m.matches((*it).second->nick + "!" +
505 (*it).second->userhost) &&
506 (*it).second->getProt() < User::NO_KICK)
507 QUEUE->sendKick(channel, (*it).second->nick, reason);
508 } else {
509 User * u = c->getUser(who);
510 if (!u)
511 return UserNotFound(who, channel);
512 if (u->getProt() < User::NO_KICK)
513 QUEUE->sendKick(channel, who, reason);
514 else
515 return UserProtected(who, channel);
516 }
517
518 return Ok;
519 }
520
521 Message
522 Commands::KickBan(Bot *bot, String channel, String who, String reason)
523 {
524 CHECK_CONNECTION;
525
526 Message m = Commands::Ban(bot, channel, who);
527
528 if (m.getCode() == 0)
529 m = Commands::Kick(bot, channel, who, reason);
530
531 return m;
532 }
533
534 Message
535 Commands::Lock(Bot *bot, String channel)
536 {
537 CHECK_CONNECTION;
538
539 Channel *c = CHANNEL(channel);
540
541 if (!c)
542 return NotOnChannel(channel);
543
544 c->lockedTopic = true;
545
546 return Ok;
547 }
548
549 Message
550 Commands::Mode(Bot *bot, String channel, String mode)
551 {
552 CHECK_CONNECTION;
553
554 if (!CHANNEL(channel))
555 return NotOnChannel(channel);
556
557 if (!bot->iAmOp(channel))
558 return NotChannelOp(channel);
559
560 QUEUE->sendChannelMode(String("MODE ") + channel + " " + mode);
561
562 return Ok;
563 }
564
565 Message
566 Commands::Msg(Bot *bot, String who, String message)
567 {
568 CHECK_CONNECTION;
569
570 if (who == "")
571 {
572 return EmptyAddressee;
573 }
574
575 if (Utils::channel_p(who))
576 {
577 if (!CHANNEL(who))
578 {
579 return NotOnChannel (who);
580 }
581 }
582
583 if (message == "")
584 {
585 return EmptyMessage;
586 }
587
588 // Send multi-line messages as seperate privmsgs
589 StringTokenizer st_message (message);
590
591 while (st_message.more_tokens_p ('\n'))
592 {
593 QUEUE->sendPrivmsg(who, st_message.next_token ('\n'));
594 }
595
596 return Ok;
597 }
598
599 Message
600 Commands::NextServer(Bot *bot)
601 {
602 CHECK_CONNECTION;
603
604 if (bot->serverList->size() == 0)
605 return EmptyServerList;
606
607 if (!bot->canChangeServer())
608 return CanNotChangeServer;
609
610 #ifdef USESCRIPTS
611 // Run hooks/disconnect
612 bot->botInterp->RunHooks
613 (Hook::DISCONNECT,
614 bot->serverConnection->server->getHostName (),
615 scm_list_n
616 (Utils::str2scm (bot->serverConnection->server->getHostName ()),
617 SCM_BOOL_T));
618 #endif
619
620 QUEUE->sendQuit("Changing server");
621 bot->nextServer();
622
623 return Ok;
624 }
625
626 Message
627 Commands::Nick(Bot *bot, String nick)
628 {
629 CHECK_CONNECTION;
630
631 if (nick == "" || !Utils::valid_nickname_p(bot, nick))
632 return InvalidNick(nick);
633
634 bot->wantedNickName = nick;
635 QUEUE->sendNick(nick);
636
637 return Ok;
638 }
639
640 Message
641 Commands::Notice(Bot *bot, String who, String message)
642 {
643 CHECK_CONNECTION;
644
645 if (who == "")
646 return EmptyAddressee;
647
648 // if (Utils::channel_p(who))
649 // return NotToChannel;
650
651 if (message == "")
652 return EmptyMessage;
653
654 // Send multiple lines as multiple notices
655 StringTokenizer st_message (message);
656
657 while (st_message.more_tokens_p ('\n'))
658 {
659 QUEUE->sendNotice(who, st_message.next_token ('\n'));
660 }
661
662 return Ok;
663 }
664
665 Message
666 Commands::Op(Bot *bot, String channel, String who)
667 {
668 CHECK_CONNECTION;
669
670 Channel *c = CHANNEL(channel);
671
672 if (!c)
673 return NotOnChannel(channel);
674
675 if (!bot->iAmOp(channel))
676 return NotChannelOp(channel);
677
678 if (Utils::wildcard_p(who))
679 return MassOpNotAllowed;
680
681 User *u = c->getUser(who);
682 if (!u)
683 return UserNotFound(who, channel);
684
685 ShitEntry *se = bot->shitList->getShit(who, channel);
686 if (se && se->isStillValid() && se->getShitLevel() >= ShitEntry::SHIT_NOOP)
687 return UserOnShitList(who);
688
689 QUEUE->sendChannelMode(channel, "+o", who);
690
691 return Ok;
692 }
693
694
695 Message
696 Commands::Part(Bot *bot, String channel)
697 {
698 CHECK_CONNECTION;
699
700 if (!CHANNEL(channel))
701 return NotOnChannel(channel);
702
703 wantedChannel *w = bot->wantedChannels[channel];
704 bot->wantedChannels.erase(channel);
705 delete w;
706 QUEUE->sendPart(channel);
707
708 return Ok;
709 }
710
711 Message
712 Commands::Reconnect(Bot *bot)
713 {
714 CHECK_CONNECTION;
715
716 if (!bot->canChangeServer())
717 return CanNotChangeServer;
718
719 QUEUE->sendQuit("Reconnecting");
720 bot->reconnect();
721
722 return Ok;
723 }
724
725 Message
726 Commands::Say(Bot *bot, String channel, String message)
727 {
728 return Commands::Msg (bot, channel, message);
729 }
730
731
732 Message
733 Commands::Server(Bot *bot, int number)
734 {
735 CHECK_CONNECTION;
736
737 if (number < 0 || number >= bot->serverList->size())
738 return InvalidServerNumber(number);
739
740 if (!bot->canChangeServer())
741 return CanNotChangeServer;
742
743 QUEUE->sendQuit("Changing server");
744 QUEUE->flush();
745 bot->connect(number);
746
747 return Ok;
748 }
749
750 Message
751 Commands::SetFloodRate(Bot *bot, unsigned int num_messages)
752 {
753 if (num_messages > 0)
754 {
755 bot->MAX_MESSAGES = num_messages;
756 return Ok;
757 }
758 return InvalidParameters;
759 }
760
761 Message
762 Commands::SetVersion(Bot *bot, String str)
763 {
764 if (str.length() == 0)
765 return InvalidParameters;
766
767 bot->versionString = str;
768 return Ok;
769 }
770
771 Message
772 Commands::TBan(Bot *bot, String channel, String who, int seconds)
773 {
774 CHECK_CONNECTION;
775
776 Channel *c = CHANNEL(channel);
777 String dest;
778
779 // Make sure all of the inputs are valid
780 if (!c)
781 {
782 return NotOnChannel(channel);
783 }
784
785 if (!bot->iAmOp(channel))
786 {
787 return NotChannelOp(channel);
788 }
789
790 if (seconds <= 0)
791 {
792 return InvalidTime(seconds);
793 }
794
795 // Look for user
796 if (!Utils::wildcard_p(who))
797 {
798 dest = bot->getUserhost(channel, who);
799 }
800 else
801 {
802 dest = who;
803 }
804
805 if (dest.length() == 0)
806 {
807 return UserNotFound(who, channel);
808 }
809
810
811 dest = Utils::make_wildcard(dest);
812 Mask m(dest);
813
814 // Make sure the user isn't protected from bans
815 for (std::list<UserListItem *>::iterator it = bot->userList->l.begin();
816 it != bot->userList->l.end();
817 it++)
818 {
819 if (m.matches((*it)->mask) &&
820 (*it)->channelMask.matches(channel) &&
821 (*it)->prot >= User::NO_BAN)
822 {
823 return UserProtected(who, channel);
824 }
825 }
826
827 // Clear existing bans on the user
828 for (std::vector<BanEntry *>::iterator it = c->channelBanlist.begin();
829 it != c->channelBanlist.end(); ++it)
830 {
831 if (m.matches((*it)->getMask ()))
832 {
833 QUEUE->sendChannelMode(channel, "-b", (*it)->getMask().getMask());
834 }
835 }
836
837 // Ban them
838 CHANNEL(channel)->addBan(dest, seconds);
839 QUEUE->sendChannelMode(channel, "+b", dest);
840 bot->todoList->addDeban(channel, dest, seconds);
841
842 return Ok;
843 }
844
845
846 Message
847 Commands::TKBan(Bot *bot, String channel, String who, int seconds, String reason)
848 {
849 CHECK_CONNECTION;
850
851 Message m = Commands::TBan(bot, channel, who, seconds);
852
853 if (m.getCode() == 0)
854 m = Commands::Kick(bot, channel, who, reason);
855
856 return m;
857 }
858
859
860 Message
861 Commands::Topic(Bot *bot, String channel, String topic)
862 {
863 CHECK_CONNECTION;
864
865 Channel *c = CHANNEL(channel);
866
867 if (!c)
868 return NotOnChannel(channel);
869
870 if (!bot->iAmOp(channel) && !(c->channelMode & Channel::TOPIC_RESTRICTED))
871 return CanNotChangeTopic(channel);
872
873 if (c->lockedTopic)
874 return TopicLocked(channel);
875
876 QUEUE->sendTopic(channel, topic);
877
878 return Ok;
879 }
880
881
882 Message
883 Commands::Unlock(Bot *bot, String channel)
884 {
885 CHECK_CONNECTION;
886
887 Channel *c = CHANNEL(channel);
888
889 if (!c)
890 return NotOnChannel(channel);
891
892 c->lockedTopic = false;
893
894 return Ok;
895 }
896
897 Message
898 Commands::Who (Bot *bot, String target)
899 {
900 CHECK_CONNECTION;
901
902 QUEUE->sendWho (target);
903
904 return Ok;
905 }
906
907 Message
908 Commands::Whois (Bot *bot, String nick)
909 {
910 CHECK_CONNECTION;
911
912 if (!Utils::valid_nickname_p (bot, nick))
913 {
914 return InvalidNick (nick);
915 }
916 else
917 {
918 QUEUE->sendWhois (nick);
919 return Ok;
920 }
921 }
922
923