/*
* 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_whois.c 1340 2005-10-03 10:38:21Z sirvulcan $
*/
/*
* 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_reply.h"
#include "ircd_string.h"
#include "ircd_struct.h"
#include "list.h"
#include "match.h"
#include "msg.h"
#include "numeric.h"
#include "numnicks.h"
#include "s_conf.h"
#include "s_user.h"
#include "send.h"
#include "whocmds.h"
#include <assert.h>
#include <string.h>
/*
* 2000-07-01: Isomer
* * Rewritten to make this understandable
* * You can nolonger /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) && !IsNoChan(acptr)) || (acptr==sptr))
{
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 (!(IsOper(sptr) && IsLocalChannel(chptr->chname)) &&
(!ShowChannel(sptr, chptr) && !IsOper(sptr)))
continue;
if (!HasPriv(sptr, PRIV_SEE_SECRET_CHAN) &&
!ShowChannel(sptr, chptr))
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) && (parc == 2) &&
feature_bool(FEAT_HIS_WHOIS_LOCALCHAN) && (acptr != sptr) &&
!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 (IsOper(sptr) && !ShowChannel(sptr,chptr))
*(buf + len++) = '*';
if (IsZombie(chan))
*(buf + len++) = '!';
else if (IsChanOp(chan))
*(buf + len++) = '@';
else if (IsHalfOp(chan))
*(buf + len++) = '%';
else if (HasVoice(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, IsAdmin(acptr) ?
feature_str(FEAT_WHOIS_ADMIN) :
feature_str(FEAT_WHOIS_OPER));
if (IsWhois(acptr) && (sptr != acptr))
sendcmdto_one(&me, CMD_NOTICE, acptr,
"%C :*** Notice -- %s (%s@%s) did a /whois on you.",
acptr, cli_name(sptr), cli_user(sptr)->username,
cli_user(sptr)->host);
}
if (IsAccount(acptr))
send_reply(sptr, RPL_WHOISACCOUNT, name, user->account);
if ((((feature_int(FEAT_HOST_HIDING_STYLE) == 1) ?
HasHiddenHost(acptr) : IsHiddenHost(acptr)) ||
HasSetHost(acptr)) && (IsAnOper(sptr) || acptr == sptr))
send_reply(sptr, RPL_WHOISACTUALLY, name, user->realusername,
user->realhost, ircd_ntoa((const char*) &(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 (IsChannelService(acptr))
send_reply(sptr, RPL_WHOISSERVICE, name, feature_str(FEAT_WHOIS_SERVICE));
if (IsAccountOnly(acptr))
send_reply(sptr, RPL_WHOISACCOUNTONLY, name);
if (IsBot(acptr))
send_reply(sptr, RPL_WHOISBOT, name);
if (IsDNSBL(acptr)) {
struct SLink* dp;
if (EmptyString(cli_dnsbls(acptr))) {
for (dp = cli_sdnsbls(acptr); dp; dp = dp->next) {
if (EmptyString(cli_dnsbls(acptr)))
strcat(cli_dnsbls(acptr), dp->value.cp);
else {
strcat(cli_dnsbls(acptr), ", ");
strcat(cli_dnsbls(acptr), dp->value.cp);
}
}
}
send_reply(sptr, RPL_WHOISDNSBL, name, cli_dnsbls(acptr));
}
if (IsSSL(acptr) && ((parc >= 3) || (acptr == sptr) ||
IsAnOper(sptr)))
send_reply(sptr, RPL_WHOISSSL, name);
if (user->swhois)
send_reply(sptr, RPL_WHOISSPECIAL, name, user->swhois);
if (MyConnect(acptr) &&
(IsAnOper(sptr) ||
(!IsNoIdle(acptr) && (!feature_bool(FEAT_HIS_WHOIS_IDLETIME) ||
sptr == acptr || 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 concidering */
struct User *user; /* the user portion of the client */
char *name; /* the name of this 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 hidious! */
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 hidious
* hack?
*/
if (IsMe(acptr)) {
assert(!cli_next(acptr));
break;
}
/*
* 'Rules' established for sending a WHOIS reply:
*
* - if wildcards are being used dont send a reply if
* the querier isnt 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);
name = (!*(cli_name(acptr))) ? "?" : cli_name(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 */
wildscount++;
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;
}
syntax highlighted by Code2HTML, v. 0.9.1