Split user list from Channel into ChannelUserList
[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
cfa82921 20#include "Commands.H"
21
71cf2688 22#include <functional>
23
cfa82921 24#include "BanEntry.H"
25#include "Bot.H"
26#include "ChannelList.H"
cb21075d 27#include "Macros.H"
28#include "Message.H"
cfa82921 29#include "Server.H"
30#include "ServerConnection.H"
31#include "ServerList.H"
32#include "ServerQueueItem.H"
33#include "ShitEntry.H"
34#include "ShitList.H"
6b7614a8 35#include "StringTokenizer.H"
cfa82921 36#include "User.H"
37#include "UserList.H"
38#include "Utils.H"
39
40#ifdef USESCRIPTS
41#include "BotInterp.H"
42#endif
cb21075d 43
cb21075d 44
45#define CHECK_CONNECTION if (!bot->serverConnection) return NotConnected
46
47Message
48Commands::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
672b7d4e 58 Commands::CTCP (bot, channel, "ACTION", message);
cb21075d 59
60 return Ok;
61}
62
63Message
64Commands::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
a6339323 78 if (!Utils::wildcard_p(who))
cb21075d 79 {
0316e2c1 80 mask = bot->getUserhost("", who);
81 if (mask.length() == 0)
82 {
83 return NotFound(who);
84 }
85 }
cb21075d 86 // Aha! This was before the brace...segfault gone
a6339323 87 mask = Utils::make_wildcard(mask);
cb21075d 88
89 if (bot->userList->isInUserList(mask, maskChannel))
0316e2c1 90 {
91 return AlreadyInUserlist(mask, maskChannel);
92 }
cb21075d 93
94 bot->userList->addUser(mask, maskChannel, level, prot, aop,
95 expire, password);
96 bot->rehash();
97
98 return Ok;
99}
100
101
102Message
103Commands::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
114Message
115Commands::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
a6339323 127 if (!Utils::wildcard_p(mask)) {
cb21075d 128 mask = bot->getUserhost("", who);
129 if (mask.length() == 0)
130 return NotFound(who);
a6339323 131 mask = Utils::make_wildcard(mask);
cb21075d 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
145Message
146Commands::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
a6339323 160 if (!Utils::wildcard_p(who))
cb21075d 161 dest = bot->getUserhost(channel, who);
162 else
163 dest = who;
164
165 if (dest.length() == 0)
166 return NotFound(who);
167
a6339323 168 dest = Utils::make_wildcard(dest);
cb21075d 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);
cb21075d 178
f9723c92 179 c->delBan (m);
180 c->addBan (m, -1);
181
cb21075d 182 return Ok;
183}
184
672b7d4e 185Message
186Commands::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
223Message
224Commands::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
cb21075d 263Message
264Commands::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
277Message
278Commands::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
a6339323 292 if (!Utils::wildcard_p(who))
cb21075d 293 dest = bot->getUserhost(channel, who);
294 else
295 dest = who;
296
297 if (dest.length() == 0)
298 return UserNotFound(who, channel);
299
a6339323 300 dest = Utils::make_wildcard(dest);
cb21075d 301 Mask m(dest);
f9723c92 302
303 BanList::MatchList matches = c->channelBanlist.find_matches (m);
cb21075d 304
f9723c92 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 }
cb21075d 316
317 return Ok;
318}
319
320Message
321Commands::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
331Message
332Commands::DelUser(Bot *bot, String who, String maskChannel)
333{
334 if (who.length() == 0 || maskChannel.length() == 0)
335 return InvalidParameters;
336
337 String dest;
338
a6339323 339 if (!Utils::wildcard_p(who)) {
cb21075d 340 dest = bot->getUserhost("", who);
341 if (dest.length() == 0)
342 return NotFound(who);
a6339323 343 dest = Utils::make_wildcard(who);
cb21075d 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
355Message
356Commands::DelShit(Bot *bot, String who, String maskChannel)
357{
358 if (who.length() == 0 || maskChannel.length() == 0)
359 return InvalidParameters;
360
361 String dest;
362
a6339323 363 if (!Utils::wildcard_p(who)) {
cb21075d 364 dest = bot->getUserhost("", who);
365 if (dest.length() == 0)
366 return NotFound(who);
a6339323 367 dest = Utils::make_wildcard(who);
cb21075d 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
71cf2688 378Commands::deop_wildcard::deop_wildcard
379(Bot *b, Mask &m, String &c)
380 : bot (b), mask (m), channel (c)
381{ }
382
383void
384Commands::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
cb21075d 392Message
393Commands::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
a6339323 405 if (!Utils::wildcard_p(who)) {
71cf2688 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 }
cb21075d 420 } else {
71cf2688 421 Mask m (who);
422 deop_wildcard f (bot, m, channel);
423 c->for_each_channel_users (f);
cb21075d 424 }
cb21075d 425 return Ok;
426}
427
428Message
429Commands::Die(Bot *bot, String reason)
430{
431 CHECK_CONNECTION;
432
433 QUEUE->sendQuit(reason);
434 bot->stop = true;
435
436 return Ok;
437}
438
439Message
440Commands::Do(Bot *bot, String command)
441{
442 CHECK_CONNECTION;
443
444 QUEUE->addLine(command, 0, 0, ServerQueueItem::OTHER);
445 return Ok;
446}
447
448Message
449Commands::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
461Message
462Commands::Join(Bot *bot, String channel, String key)
463{
464 CHECK_CONNECTION;
465
a6339323 466 if (!Utils::valid_channel_name_p(channel))
cb21075d 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])
71cf2688 473 bot->wantedChannels[channel]->key = static_cast<std::string> (key);
cb21075d 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
483Message
484Commands::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
71cf2688 498Commands::kick_wildcard::kick_wildcard
499(Bot *b, Mask &m, String &c, String &r)
500 : bot (b), mask (m), channel (c), reason (r)
501{ }
502
503void
504Commands::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
cb21075d 511Message
512Commands::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
a6339323 524 if (Utils::wildcard_p(who)) {
cb21075d 525 Mask m(who);
71cf2688 526 kick_wildcard f (bot, m, channel, reason);
527 c->for_each_channel_users (f);
cb21075d 528 } else {
71cf2688 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 }
cb21075d 542 }
543
544 return Ok;
545}
546
547Message
548Commands::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
560Message
561Commands::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
575Message
576Commands::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
591Message
592Commands::Msg(Bot *bot, String who, String message)
593{
594 CHECK_CONNECTION;
595
596 if (who == "")
5c73c60a 597 {
598 return EmptyAddressee;
599 }
cb21075d 600
a6339323 601 if (Utils::channel_p(who))
5c73c60a 602 {
4cc479d8 603 if (!CHANNEL(who))
604 {
605 return NotOnChannel (who);
606 }
5c73c60a 607 }
cb21075d 608
609 if (message == "")
5c73c60a 610 {
611 return EmptyMessage;
612 }
cb21075d 613
6b7614a8 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 }
cb21075d 621
622 return Ok;
623}
624
625Message
626Commands::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;
ae97d6ec 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
cb21075d 645
646 QUEUE->sendQuit("Changing server");
647 bot->nextServer();
648
649 return Ok;
650}
651
652Message
653Commands::Nick(Bot *bot, String nick)
654{
655 CHECK_CONNECTION;
656
6b7614a8 657 if (nick == "" || !Utils::valid_nickname_p(bot, nick))
cb21075d 658 return InvalidNick(nick);
659
660 bot->wantedNickName = nick;
661 QUEUE->sendNick(nick);
662
663 return Ok;
664}
665
666Message
667Commands::Notice(Bot *bot, String who, String message)
668{
669 CHECK_CONNECTION;
670
671 if (who == "")
672 return EmptyAddressee;
673
5aec4622 674 // if (Utils::channel_p(who))
675 // return NotToChannel;
cb21075d 676
677 if (message == "")
678 return EmptyMessage;
679
6b7614a8 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 }
cb21075d 687
688 return Ok;
689}
690
691Message
692Commands::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
a6339323 704 if (Utils::wildcard_p(who))
cb21075d 705 return MassOpNotAllowed;
71cf2688 706
707 try
708 {
709 User u = c->getUser(who);
cb21075d 710
71cf2688 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 }
cb21075d 721
722 return Ok;
723}
724
725
726Message
727Commands::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
742Message
743Commands::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
756Message
757Commands::Say(Bot *bot, String channel, String message)
758{
4cc479d8 759 return Commands::Msg (bot, channel, message);
cb21075d 760}
761
762
763Message
764Commands::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
e171dcce 781Message
782Commands::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
cb21075d 792Message
793Commands::SetVersion(Bot *bot, String str)
794{
795 if (str.length() == 0)
796 return InvalidParameters;
797
798 bot->versionString = str;
799 return Ok;
800}
801
802Message
803Commands::TBan(Bot *bot, String channel, String who, int seconds)
804{
805 CHECK_CONNECTION;
806
807 Channel *c = CHANNEL(channel);
cf8ea873 808 String dest;
cb21075d 809
cf8ea873 810 // Make sure all of the inputs are valid
cb21075d 811 if (!c)
f9723c92 812 return NotOnChannel(channel);
cb21075d 813 if (!bot->iAmOp(channel))
f9723c92 814 return NotChannelOp(channel);
cb21075d 815 if (seconds <= 0)
f9723c92 816 return InvalidTime(seconds);
817
cf8ea873 818 // Look for user
a6339323 819 if (!Utils::wildcard_p(who))
f9723c92 820 dest = bot->getUserhost(channel, who);
cb21075d 821 else
f9723c92 822 dest = who;
823
cb21075d 824 if (dest.length() == 0)
f9723c92 825 return UserNotFound(who, channel);
cb21075d 826
a6339323 827 dest = Utils::make_wildcard(dest);
cb21075d 828 Mask m(dest);
829
cf8ea873 830 // Make sure the user isn't protected from bans
cb21075d 831 for (std::list<UserListItem *>::iterator it = bot->userList->l.begin();
832 it != bot->userList->l.end();
833 it++)
cf8ea873 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 }
cb21075d 842
cb21075d 843
f9723c92 844 // c->delBan (dest);
845 c->addBan(dest, seconds);
cf8ea873 846
847 return Ok;
cb21075d 848}
849
850
851Message
852Commands::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
865Message
866Commands::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
887Message
888Commands::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}
c6e7af05 901
902Message
903Commands::Who (Bot *bot, String target)
904{
905 CHECK_CONNECTION;
906
907 QUEUE->sendWho (target);
908
909 return Ok;
910}
911
912Message
913Commands::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