X-Git-Url: http://git.pk910.de/?p=ircu2.10.12-pk.git;a=blobdiff_plain;f=ircd%2Fm_whois.c;fp=ircd%2Fm_whois.c;h=01c9db55db3d1d8311a6b89f76bc40ffa79a9930;hp=0000000000000000000000000000000000000000;hb=0400a5a6479398d82526785c18c0df8bc8b92dce;hpb=d17e10da972ce5776c60b4c317267c6abe0e1ead diff --git a/ircd/m_whois.c b/ircd/m_whois.c new file mode 100644 index 0000000..01c9db5 --- /dev/null +++ b/ircd/m_whois.c @@ -0,0 +1,500 @@ +/* + * IRC - Internet Relay Chat, ircd/m_whois.c + * Copyright (C) 1990 Jarkko Oikarinen and + * University of Oulu, Computing Center + * + * See file AUTHORS in IRC package for additional names of + * the programmers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +/* + * m_functions execute protocol messages on this server: + * + * cptr is always NON-NULL, pointing to a *LOCAL* client + * structure (with an open socket connected!). This + * identifies the physical socket where the message + * originated (or which caused the m_function to be + * executed--some m_functions may call others...). + * + * sptr is the source of the message, defined by the + * prefix part of the message if present. If not + * or prefix not found, then sptr==cptr. + * + * (!IsServer(cptr)) => (cptr == sptr), because + * prefixes are taken *only* from servers... + * + * (IsServer(cptr)) + * (sptr == cptr) => the message didn't + * have the prefix. + * + * (sptr != cptr && IsServer(sptr) means + * the prefix specified servername. (?) + * + * (sptr != cptr && !IsServer(sptr) means + * that message originated from a remote + * user (not local). + * + * combining + * + * (!IsServer(sptr)) means that, sptr can safely + * taken as defining the target structure of the + * message in this server. + * + * *Always* true (if 'parse' and others are working correct): + * + * 1) sptr->from == cptr (note: cptr->from == cptr) + * + * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr + * *cannot* be a local connection, unless it's + * actually cptr!). [MyConnect(x) should probably + * be defined as (x == x->from) --msa ] + * + * parc number of variable parameter strings (if zero, + * parv is allowed to be NULL) + * + * parv a NULL terminated list of parameter pointers, + * + * parv[0], sender (prefix string), if not present + * this points to an empty string. + * parv[1]...parv[parc-1] + * pointers to additional parameters + * parv[parc] == NULL, *always* + * + * note: it is guaranteed that parv[0]..parv[parc-1] are all + * non-NULL pointers. + */ +#include "config.h" + +#include "channel.h" +#include "client.h" +#include "hash.h" +#include "ircd.h" +#include "ircd_features.h" +#include "ircd_log.h" +#include "ircd_reply.h" +#include "ircd_string.h" +#include "match.h" +#include "msg.h" +#include "numeric.h" +#include "numnicks.h" +#include "s_user.h" +#include "send.h" +#include "whocmds.h" + +/* #include -- Now using assert in ircd_log.h */ +#include + +/* + * 2000-07-01: Isomer + * * Rewritten to make this understandable + * * You can no longer /whois unregistered clients. + * + * + * General rules: + * /whois nick always shows the nick. + * /whois wild* shows the nick if: + * * they aren't +i and aren't on any channels. + * * they are on a common channel. + * * they aren't +i and are on a public channel. (not +p and not +s) + * * they aren't +i and are on a private channel. (+p but not +s) + * Or to look at it another way (I think): + * * +i users are only shown if your on a common channel. + * * users on +s channels only aren't shown. + * + * whois doesn't show what channels a +k client is on, for the reason that + * /whois X or /whois W floods a user off the server. :) + * + * nb: if the code and this comment disagree, the codes right and I screwed + * up. + */ + +/* + * Send whois information for acptr to sptr + */ +static void do_whois(struct Client* sptr, struct Client *acptr, int parc) +{ + struct Client *a2cptr=0; + struct Channel *chptr=0; + int mlen; + int len; + static char buf[512]; + + const struct User* user = cli_user(acptr); + const char* name = (!*(cli_name(acptr))) ? "?" : cli_name(acptr); + a2cptr = feature_bool(FEAT_HIS_WHOIS_SERVERNAME) && !IsAnOper(sptr) + && sptr != acptr ? &his : user->server; + assert(user); + send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host, + cli_info(acptr)); + + /* Display the channels this user is on. */ + if (!IsChannelService(acptr)) + { + struct Membership* chan; + mlen = strlen(cli_name(&me)) + strlen(cli_name(sptr)) + 12 + strlen(name); + len = 0; + *buf = '\0'; + for (chan = user->channel; chan; chan = chan->next_channel) + { + chptr = chan->channel; + + if (!ShowChannel(sptr, chptr) + && !(IsOper(sptr) && IsLocalChannel(chptr->chname))) + continue; + + if (acptr != sptr && IsZombie(chan)) + continue; + + /* Don't show local channels when HIS is defined, unless it's a + * remote WHOIS --ULtimaTe_ + */ + if (IsLocalChannel(chptr->chname) && (acptr != sptr) && (parc == 2) + && feature_bool(FEAT_HIS_WHOIS_LOCALCHAN) && !IsAnOper(sptr)) + continue; + + if (len+strlen(chptr->chname) + mlen > BUFSIZE - 5) + { + send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS, "%s :%s", name, buf); + *buf = '\0'; + len = 0; + } + if (IsDeaf(acptr)) + *(buf + len++) = '-'; + if (!ShowChannel(sptr, chptr)) + *(buf + len++) = '*'; + if (IsDelayedJoin(chan) && (sptr != acptr)) + *(buf + len++) = '<'; + else if (IsChanOp(chan)) + *(buf + len++) = '@'; + else if (HasVoice(chan)) + *(buf + len++) = '+'; + else if (IsZombie(chan)) + *(buf + len++) = '!'; + if (len) + *(buf + len) = '\0'; + strcpy(buf + len, chptr->chname); + len += strlen(chptr->chname); + strcat(buf + len, " "); + len++; + } + if (buf[0] != '\0') + send_reply(sptr, RPL_WHOISCHANNELS, name, buf); + } + + send_reply(sptr, RPL_WHOISSERVER, name, cli_name(a2cptr), + cli_info(a2cptr)); + + if (user) + { + if (user->away) + send_reply(sptr, RPL_AWAY, name, user->away); + + if (SeeOper(sptr,acptr)) + send_reply(sptr, RPL_WHOISOPERATOR, name); + + if (IsAccount(acptr)) + send_reply(sptr, RPL_WHOISACCOUNT, name, user->account); + + if (HasHiddenHost(acptr) && (IsAnOper(sptr) || acptr == sptr)) + send_reply(sptr, RPL_WHOISACTUALLY, name, user->username, + user->realhost, ircd_ntoa(&cli_ip(acptr))); + + /* Hint: if your looking to add more flags to a user, eg +h, here's + * probably a good place to add them :) + */ + + if (MyConnect(acptr) && (!feature_bool(FEAT_HIS_WHOIS_IDLETIME) || + (sptr == acptr || IsAnOper(sptr) || parc >= 3))) + send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last, + cli_firsttime(acptr)); + } +} + +/* + * Search and return as many people as matched by the wild 'nick'. + * returns the number of people found (or, obviously, 0, if none where + * found). + */ +static int do_wilds(struct Client* sptr, char *nick, int count, int parc) +{ + struct Client *acptr; /* Current client we're considering */ + struct User *user; /* the user portion of the client */ + struct Membership* chan; + int invis; /* does +i apply? */ + int member; /* Is this user on any channels? */ + int showperson; /* Should we show this person? */ + int found = 0 ; /* How many were found? */ + + /* Ech! This is hideous! */ + for (acptr = GlobalClientList; (acptr = next_client(acptr, nick)); + acptr = cli_next(acptr)) + { + if (!IsRegistered(acptr)) + continue; + + if (IsServer(acptr)) + continue; + /* + * I'm always last :-) and acptr->next == 0!! + * + * Isomer: Does this strike anyone else as being a horrible hideous + * hack? + */ + if (IsMe(acptr)) { + assert(!cli_next(acptr)); + break; + } + + /* + * 'Rules' established for sending a WHOIS reply: + * + * - if wildcards are being used don't send a reply if + * the querier isn't any common channels and the + * client in question is invisible. + * + * - only send replies about common or public channels + * the target user(s) are on; + */ + user = cli_user(acptr); + assert(user); + + invis = (acptr != sptr) && IsInvisible(acptr); + member = (user && user->channel) ? 1 : 0; + showperson = !invis && !member; + + /* Should we show this person now? */ + if (showperson) { + found++; + do_whois(sptr, acptr, parc); + if (count+found>MAX_WHOIS_LINES) + return found; + continue; + } + + /* Step through the channels this user is on */ + for (chan = user->channel; chan; chan = chan->next_channel) + { + struct Channel *chptr = chan->channel; + + /* If this is a public channel, show the person */ + if (!invis && PubChannel(chptr)) { + showperson = 1; + break; + } + + /* if this channel is +p and not +s, show them */ + if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr)) { + showperson = 1; + break; + } + + member = find_channel_member(sptr, chptr) ? 1 : 0; + if (invis && !member) + continue; + + /* If sptr isn't really on this channel, skip it */ + if (IsZombie(chan)) + continue; + + /* Is this a common channel? */ + if (member) { + showperson = 1; + break; + } + } /* of for (chan in channels) */ + + /* Don't show this person */ + if (!showperson) + continue; + + do_whois(sptr, acptr, parc); + found++; + if (count+found>MAX_WHOIS_LINES) + return found; + } /* of global client list */ + + return found; +} + +/* + * m_whois - generic message handler + * + * parv[0] = sender prefix + * parv[1] = nickname masklist + * + * or + * + * parv[1] = target server, or a nickname representing a server to target. + * parv[2] = nickname masklist + */ +int m_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) +{ + char* nick; + char* tmp; + char* p = 0; + int found = 0; + int total = 0; + int wildscount = 0; + + if (parc < 2) + { + send_reply(sptr, ERR_NONICKNAMEGIVEN); + return 0; + } + + if (parc > 2) + { + /* For convenience: Accept a nickname as first parameter, by replacing + * it with the correct servername - as is needed by hunt_server(). + * This is the secret behind the /whois nick nick trick. + */ + if (feature_int(FEAT_HIS_REMOTE)) + { + /* If remote queries are disabled, then use the *second* parameter of + * of whois, so /whois nick nick still works. + */ + if (!IsAnOper(sptr)) + { + if (!FindUser(parv[2])) + { + send_reply(sptr, ERR_NOSUCHNICK, parv[2]); + send_reply(sptr, RPL_ENDOFWHOIS, parv[2]); + return 0; + } + parv[1] = parv[2]; + } + } + + if (hunt_server_cmd(sptr, CMD_WHOIS, cptr, 0, "%C :%s", 1, parc, parv) != + HUNTED_ISME) + return 0; + + parv[1] = parv[2]; + } + + for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0) + { + int wilds; + + found = 0; + + collapse(nick); + + wilds = (strchr(nick, '?') || strchr(nick, '*')); + if (!wilds) { + struct Client *acptr = 0; + /* No wildcards */ + acptr = FindUser(nick); + if (acptr && !IsServer(acptr)) { + do_whois(sptr, acptr, parc); + found = 1; + } + } + else /* wilds */ + { + if (++wildscount > 3) { + send_reply(sptr, ERR_QUERYTOOLONG, parv[1]); + break; + } + found=do_wilds(sptr, nick, total, parc); + } + + if (!found) + send_reply(sptr, ERR_NOSUCHNICK, nick); + total+=found; + if (total >= MAX_WHOIS_LINES) { + send_reply(sptr, ERR_QUERYTOOLONG, parv[1]); + break; + } + if (p) + p[-1] = ','; + } /* of tokenised parm[1] */ + send_reply(sptr, RPL_ENDOFWHOIS, parv[1]); + + return 0; +} + +/* + * ms_whois - server message handler + * + * parv[0] = sender prefix + * parv[1] = nickname masklist + * + * or + * + * parv[1] = target server, or a nickname representing a server to target. + * parv[2] = nickname masklist + */ +int ms_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) +{ + char* nick; + char* tmp; + char* p = 0; + int found = 0; + int total = 0; + + if (parc < 2) + { + send_reply(sptr, ERR_NONICKNAMEGIVEN); + return 0; + } + + if (parc > 2) + { + if (hunt_server_cmd(sptr, CMD_WHOIS, cptr, 0, "%C :%s", 1, parc, parv) != + HUNTED_ISME) + return 0; + parv[1] = parv[2]; + } + + total = 0; + + for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0) + { + struct Client *acptr = 0; + + found = 0; + + collapse(nick); + + + acptr = FindUser(nick); + if (acptr && !IsServer(acptr)) { + found++; + do_whois(sptr, acptr, parc); + } + + if (!found) + send_reply(sptr, ERR_NOSUCHNICK, nick); + + total+=found; + + if (total >= MAX_WHOIS_LINES) { + send_reply(sptr, ERR_QUERYTOOLONG, parv[1]); + break; + } + + if (p) + p[-1] = ','; + } /* of tokenised parm[1] */ + send_reply(sptr, RPL_ENDOFWHOIS, parv[1]); + + return 0; +} +