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