2 * IRC - Internet Relay Chat, ircd/m_whois.c
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Computing Center
6 * See file AUTHORS in IRC package for additional names of
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 1, or (at your option)
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 * m_functions execute protocol messages on this server:
29 * cptr is always NON-NULL, pointing to a *LOCAL* client
30 * structure (with an open socket connected!). This
31 * identifies the physical socket where the message
32 * originated (or which caused the m_function to be
33 * executed--some m_functions may call others...).
35 * sptr is the source of the message, defined by the
36 * prefix part of the message if present. If not
37 * or prefix not found, then sptr==cptr.
39 * (!IsServer(cptr)) => (cptr == sptr), because
40 * prefixes are taken *only* from servers...
43 * (sptr == cptr) => the message didn't
46 * (sptr != cptr && IsServer(sptr) means
47 * the prefix specified servername. (?)
49 * (sptr != cptr && !IsServer(sptr) means
50 * that message originated from a remote
55 * (!IsServer(sptr)) means that, sptr can safely
56 * taken as defining the target structure of the
57 * message in this server.
59 * *Always* true (if 'parse' and others are working correct):
61 * 1) sptr->from == cptr (note: cptr->from == cptr)
63 * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
64 * *cannot* be a local connection, unless it's
65 * actually cptr!). [MyConnect(x) should probably
66 * be defined as (x == x->from) --msa ]
68 * parc number of variable parameter strings (if zero,
69 * parv is allowed to be NULL)
71 * parv a NULL terminated list of parameter pointers,
73 * parv[0], sender (prefix string), if not present
74 * this points to an empty string.
75 * parv[1]...parv[parc-1]
76 * pointers to additional parameters
77 * parv[parc] == NULL, *always*
79 * note: it is guaranteed that parv[0]..parv[parc-1] are all
84 * No need to include handlers.h here the signatures must match
85 * and we don't need to force a rebuild of all the handlers everytime
86 * we add a new one to the list. --Bleep
94 #include "ircd_reply.h"
95 #include "ircd_string.h"
108 * m_whois - generic message handler
110 * parv[0] = sender prefix
111 * parv[1] = nickname masklist
115 * parv[1] = target server
116 * parv[2] = nickname masklist
118 int m_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
121 struct Client* acptr;
122 struct Client* a2cptr;
123 struct Channel* chptr;
132 static char buf[512];
136 send_reply(sptr, ERR_NONICKNAMEGIVEN);
142 struct Client *acptr;
143 /* For convenience: Accept a nickname as first parameter, by replacing
144 it with the correct servername - as is needed by hunt_server() */
145 if (MyUser(sptr) && (acptr = FindUser(parv[1])))
146 parv[1] = acptr->user->server->name;
147 if (hunt_server_cmd(sptr, CMD_WHOIS, cptr, 0, "%C :%s", 1, parc, parv) !=
154 for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0)
156 int invis, showperson, member, wilds;
160 wilds = (strchr(nick, '?') || strchr(nick, '*'));
161 /* Do a hash lookup if the nick does not contain wilds */
165 * We're no longer allowing remote users to generate requests with wildcards.
167 if (!MyConnect(sptr))
169 for (acptr = GlobalClientList; (acptr = next_client(acptr, nick));
172 if (!IsRegistered(acptr) || IsServer(acptr))
175 * I'm always last :-) and acptr->next == 0!!
180 * 'Rules' established for sending a WHOIS reply:
182 * - if wildcards are being used dont send a reply if
183 * the querier isnt any common channels and the
184 * client in question is invisible and wildcards are
185 * in use (allow exact matches only);
187 * - only send replies about common or public channels
188 * the target user(s) are on;
191 name = (!*acptr->name) ? "?" : acptr->name;
193 invis = acptr != sptr && IsInvisible(acptr);
194 member = (user && user->channel) ? 1 : 0;
195 showperson = (wilds && !invis && !member) || !wilds;
197 struct Membership* chan;
198 for (chan = user->channel; chan; chan = chan->next_channel)
200 chptr = chan->channel;
201 member = find_channel_member(sptr, chptr) ? 1 : 0;
202 if (invis && !member)
206 if (member || (!invis && PubChannel(chptr)))
211 if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
220 a2cptr = user->server;
221 send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host,
227 send_reply(sptr, RPL_WHOISUSER, name, "<unknown>", "<unknown>",
234 if (user && !IsChannelService(acptr))
236 struct Membership* chan;
237 mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name);
240 for (chan = user->channel; chan; chan = chan->next_channel)
242 chptr = chan->channel;
243 if (ShowChannel(sptr, chptr) &&
244 (acptr == sptr || !IsZombie(chan)))
246 if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5)
248 send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS,
249 "%s :%s", name, buf);
254 *(buf + len++) = '-';
255 if (is_chan_op(acptr, chptr))
256 *(buf + len++) = '@';
257 else if (has_voice(acptr, chptr))
258 *(buf + len++) = '+';
259 else if (IsZombie(chan))
260 *(buf + len++) = '!';
263 strcpy(buf + len, chptr->chname);
264 len += strlen(chptr->chname);
265 strcat(buf + len, " ");
270 send_reply(sptr, RPL_WHOISCHANNELS, name, buf);
273 send_reply(sptr, RPL_WHOISSERVER, name, a2cptr->name, a2cptr->info);
278 send_reply(sptr, RPL_AWAY, name, user->away);
281 send_reply(sptr, RPL_WHOISOPERATOR, name);
283 if (MyConnect(acptr))
284 send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last,
287 if (found == 2 || total++ >= MAX_WHOIS_LINES)
294 if ((acptr = FindUser(nick)))
296 found = 2; /* Make sure we exit the loop after passing it once */
298 name = (!*acptr->name) ? "?" : acptr->name;
299 a2cptr = user->server;
300 send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host,
306 send_reply(sptr, ERR_NOSUCHNICK, nick);
309 if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES)
312 send_reply(sptr, RPL_ENDOFWHOIS, parv[1]);
318 * ms_whois - server message handler
320 * parv[0] = sender prefix
321 * parv[1] = nickname masklist
325 * parv[1] = target server
326 * parv[2] = nickname masklist
328 int ms_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
331 struct Client* acptr;
332 struct Client* a2cptr;
333 struct Channel* chptr;
342 static char buf[512];
346 send_reply(sptr, ERR_NONICKNAMEGIVEN);
352 struct Client *acptr;
353 /* For convenience: Accept a nickname as first parameter, by replacing
354 it with the correct servername - as is needed by hunt_server() */
355 if (MyUser(sptr) && (acptr = FindUser(parv[1])))
356 parv[1] = acptr->user->server->name;
357 if (hunt_server_cmd(sptr, CMD_WHOIS, cptr, 0, "%C :%s", 1, parc, parv) !=
364 for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0)
366 int invis, showperson, member, wilds;
370 wilds = (strchr(nick, '?') || strchr(nick, '*'));
371 /* Do a hash lookup if the nick does not contain wilds */
375 * We're no longer allowing remote users to generate requests with wildcards.
377 if (!MyConnect(sptr))
379 for (acptr = GlobalClientList; (acptr = next_client(acptr, nick));
382 if (!IsRegistered(acptr) || IsServer(acptr))
385 * I'm always last :-) and acptr->next == 0!!
390 * 'Rules' established for sending a WHOIS reply:
392 * - if wildcards are being used dont send a reply if
393 * the querier isnt any common channels and the
394 * client in question is invisible and wildcards are
395 * in use (allow exact matches only);
397 * - only send replies about common or public channels
398 * the target user(s) are on;
401 name = (!*acptr->name) ? "?" : acptr->name;
403 invis = acptr != sptr && IsInvisible(acptr);
404 member = (user && user->channel) ? 1 : 0;
405 showperson = (wilds && !invis && !member) || !wilds;
407 struct Membership* chan;
408 for (chan = user->channel; chan; chan = chan->next_channel)
410 chptr = chan->channel;
411 member = find_channel_member(sptr, chptr) ? 1 : 0;
412 if (invis && !member)
416 if (member || (!invis && PubChannel(chptr)))
421 if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
430 a2cptr = user->server;
431 send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host,
437 send_reply(sptr, RPL_WHOISUSER, name, "<unknown>", "<unknown>",
444 if (user && !IsChannelService(acptr))
446 struct Membership* chan;
447 mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name);
450 for (chan = user->channel; chan; chan = chan->next_channel)
452 chptr = chan->channel;
453 if (ShowChannel(sptr, chptr) &&
454 (acptr == sptr || !IsZombie(chan)))
456 if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5)
458 send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS,
459 "%s :%s", name, buf);
464 *(buf + len++) = '-';
465 if (is_chan_op(acptr, chptr))
466 *(buf + len++) = '@';
467 else if (has_voice(acptr, chptr))
468 *(buf + len++) = '+';
469 else if (IsZombie(chan))
470 *(buf + len++) = '!';
473 strcpy(buf + len, chptr->chname);
474 len += strlen(chptr->chname);
475 strcat(buf + len, " ");
480 send_reply(sptr, RPL_WHOISCHANNELS, name, buf);
483 send_reply(sptr, RPL_WHOISSERVER, name, a2cptr->name, a2cptr->info);
488 send_reply(sptr, RPL_AWAY, name, user->away);
491 send_reply(sptr, RPL_WHOISOPERATOR, name);
493 if (MyConnect(acptr))
494 send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last,
497 if (found == 2 || total++ >= MAX_WHOIS_LINES)
504 if ((acptr = FindUser(nick)))
506 found = 2; /* Make sure we exit the loop after passing it once */
508 name = (!*acptr->name) ? "?" : acptr->name;
509 a2cptr = user->server;
510 send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host,
516 send_reply(sptr, ERR_NOSUCHNICK, nick);
519 if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES)
522 send_reply(sptr, RPL_ENDOFWHOIS, parv[1]);
528 * mo_whois - oper message handler
530 * parv[0] = sender prefix
531 * parv[1] = nickname masklist
535 * parv[1] = target server
536 * parv[2] = nickname masklist
538 int mo_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
541 struct Client* acptr;
542 struct Client* a2cptr;
543 struct Channel* chptr;
552 static char buf[512];
556 send_reply(sptr, ERR_NONICKNAMEGIVEN);
562 struct Client *acptr;
563 /* For convenience: Accept a nickname as first parameter, by replacing
564 it with the correct servername - as is needed by hunt_server() */
565 if (MyUser(sptr) && (acptr = FindUser(parv[1])))
566 parv[1] = acptr->user->server->name;
567 if (hunt_server_cmd(sptr, CMD_WHOIS, cptr, 0, "%C :%s", 1, parc, parv) !=
574 for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0)
576 int invis, showperson, member, wilds;
580 wilds = (strchr(nick, '?') || strchr(nick, '*'));
581 /* Do a hash lookup if the nick does not contain wilds */
585 * We're no longer allowing remote users to generate requests with wildcards.
587 if (!MyConnect(sptr))
589 for (acptr = GlobalClientList; (acptr = next_client(acptr, nick));
592 if (!IsRegistered(acptr) || IsServer(acptr))
595 * I'm always last :-) and acptr->next == 0!!
600 * 'Rules' established for sending a WHOIS reply:
602 * - if wildcards are being used dont send a reply if
603 * the querier isnt any common channels and the
604 * client in question is invisible and wildcards are
605 * in use (allow exact matches only);
607 * - only send replies about common or public channels
608 * the target user(s) are on;
611 name = (!*acptr->name) ? "?" : acptr->name;
613 invis = acptr != sptr && IsInvisible(acptr);
614 member = (user && user->channel) ? 1 : 0;
615 showperson = (wilds && !invis && !member) || !wilds;
617 struct Membership* chan;
618 for (chan = user->channel; chan; chan = chan->next_channel)
620 chptr = chan->channel;
621 member = find_channel_member(sptr, chptr) ? 1 : 0;
622 if (invis && !member)
626 if (member || (!invis && PubChannel(chptr)))
631 if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
640 a2cptr = user->server;
641 send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host,
647 send_reply(sptr, RPL_WHOISUSER, name, "<unknown>", "<unknown>",
654 if (user && !IsChannelService(acptr))
656 struct Membership* chan;
657 mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name);
660 for (chan = user->channel; chan; chan = chan->next_channel)
662 chptr = chan->channel;
663 if (ShowChannel(sptr, chptr) &&
664 (acptr == sptr || !IsZombie(chan)))
666 if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5)
668 send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS,
669 "%s :%s", name, buf);
674 *(buf + len++) = '-';
675 if (is_chan_op(acptr, chptr))
676 *(buf + len++) = '@';
677 else if (has_voice(acptr, chptr))
678 *(buf + len++) = '+';
679 else if (IsZombie(chan))
680 *(buf + len++) = '!';
683 strcpy(buf + len, chptr->chname);
684 len += strlen(chptr->chname);
685 strcat(buf + len, " ");
690 send_reply(sptr, RPL_WHOISCHANNELS, name, buf);
693 send_reply(sptr, RPL_WHOISSERVER, name, a2cptr->name, a2cptr->info);
698 send_reply(sptr, RPL_AWAY, name, user->away);
701 send_reply(sptr, RPL_WHOISOPERATOR, name);
703 if (MyConnect(acptr))
704 send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last,
707 if (found == 2 || total++ >= MAX_WHOIS_LINES)
714 if ((acptr = FindUser(nick)))
716 found = 2; /* Make sure we exit the loop after passing it once */
718 name = (!*acptr->name) ? "?" : acptr->name;
719 a2cptr = user->server;
720 send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host,
726 send_reply(sptr, ERR_NOSUCHNICK, nick);
729 if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES)
732 send_reply(sptr, RPL_ENDOFWHOIS, parv[1]);
743 * parv[0] = sender prefix
744 * parv[1] = nickname masklist
748 * parv[1] = target server
749 * parv[2] = nickname masklist
751 int m_whois(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
754 struct Client* acptr;
755 struct Client* a2cptr;
756 struct Channel* chptr;
765 static char buf[512];
769 sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]); /* XXX DEAD */
775 struct Client *acptr;
776 /* For convenience: Accept a nickname as first parameter, by replacing
777 it with the correct servername - as is needed by hunt_server() */
778 if (MyUser(sptr) && (acptr = FindUser(parv[1])))
779 parv[1] = acptr->user->server->name;
780 if (hunt_server(0, cptr, sptr, "%s%s " TOK_WHOIS " %s :%s", 1, parc, parv) != /* XXX DEAD */
787 for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0)
789 int invis, showperson, member, wilds;
793 wilds = (strchr(nick, '?') || strchr(nick, '*'));
794 /* Do a hash lookup if the nick does not contain wilds */
798 * We're no longer allowing remote users to generate requests with wildcards.
800 if (!MyConnect(sptr))
802 for (acptr = GlobalClientList; (acptr = next_client(acptr, nick));
805 if (!IsRegistered(acptr) || IsServer(acptr))
808 * I'm always last :-) and acptr->next == NULL!!
813 * 'Rules' established for sending a WHOIS reply:
815 * - if wildcards are being used dont send a reply if
816 * the querier isnt any common channels and the
817 * client in question is invisible and wildcards are
818 * in use (allow exact matches only);
820 * - only send replies about common or public channels
821 * the target user(s) are on;
824 name = (!*acptr->name) ? "?" : acptr->name;
826 invis = acptr != sptr && IsInvisible(acptr);
827 member = (user && user->channel) ? 1 : 0;
828 showperson = (wilds && !invis && !member) || !wilds;
830 struct Membership* chan;
831 for (chan = user->channel; chan; chan = chan->next_channel)
833 chptr = chan->channel;
834 member = find_channel_member(sptr, chptr) ? 1 : 0;
835 if (invis && !member)
839 if (member || (!invis && PubChannel(chptr)))
844 if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
853 a2cptr = user->server;
854 sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name, /* XXX DEAD */
855 parv[0], name, user->username, user->host, acptr->info);
860 sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name, /* XXX DEAD */
861 parv[0], name, "<unknown>", "<unknown>", "<unknown>");
867 if (user && !IsChannelService(acptr))
869 struct Membership* chan;
870 mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name);
873 for (chan = user->channel; chan; chan = chan->next_channel)
875 chptr = chan->channel;
876 if (ShowChannel(sptr, chptr) &&
877 (acptr == sptr || !IsZombie(chan)))
879 if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5)
881 sendto_one(sptr, ":%s %d %s %s :%s", /* XXX DEAD */
882 me.name, RPL_WHOISCHANNELS, parv[0], name, buf);
887 *(buf + len++) = '-';
888 if (is_chan_op(acptr, chptr))
889 *(buf + len++) = '@';
890 else if (has_voice(acptr, chptr))
891 *(buf + len++) = '+';
892 else if (IsZombie(chan))
893 *(buf + len++) = '!';
896 strcpy(buf + len, chptr->chname);
897 len += strlen(chptr->chname);
898 strcat(buf + len, " ");
903 sendto_one(sptr, rpl_str(RPL_WHOISCHANNELS), /* XXX DEAD */
904 me.name, parv[0], name, buf);
907 sendto_one(sptr, rpl_str(RPL_WHOISSERVER), me.name, /* XXX DEAD */
908 parv[0], name, a2cptr->name, a2cptr->info);
913 sendto_one(sptr, rpl_str(RPL_AWAY), me.name, /* XXX DEAD */
914 parv[0], name, user->away);
917 sendto_one(sptr, rpl_str(RPL_WHOISOPERATOR), /* XXX DEAD */
918 me.name, parv[0], name);
920 if (MyConnect(acptr))
921 sendto_one(sptr, rpl_str(RPL_WHOISIDLE), me.name, /* XXX DEAD */
922 parv[0], name, CurrentTime - user->last, acptr->firsttime);
924 if (found == 2 || total++ >= MAX_WHOIS_LINES)
931 if ((acptr = FindUser(nick)))
933 found = 2; /* Make sure we exit the loop after passing it once */
935 name = (!*acptr->name) ? "?" : acptr->name;
936 a2cptr = user->server;
937 sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name, /* XXX DEAD */
938 parv[0], name, user->username, user->host, acptr->info);
943 sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick); /* XXX DEAD */
946 if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES)
949 sendto_one(sptr, rpl_str(RPL_ENDOFWHOIS), me.name, parv[0], parv[1]); /* XXX DEAD */