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