/************************************************************************
* IRC - Internet Relay Chat, modules/m_who.c
*
* Copyright (C) 2000-2003 TR-IRCD Development
*
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Co Center
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* This program is free softwmare; 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 2, 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.
*/
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include "s_conf.h"
#include "language.h"
#include "usermode.h"
#include "h.h"
static struct Message _msgtab[] = {
{MSG_WHO, 0, MAXPARA, M_SLOW, 0L,
m_unregistered, m_who, m_who, m_ignore, m_ignore}
};
#ifndef STATIC_MODULES
char *_version = "$Revision: 1.4 $";
void _modinit(void)
{
mod_add_cmd(_msgtab);
}
void _moddeinit(void)
{
mod_del_cmd(_msgtab);
}
#else
void m_who_init(void)
{
mod_add_cmd(_msgtab);
}
#endif
/* We do not need link traversal here, because users
* do not exist in the root channel, and it is already
* set to +tnL, after cleaning. -TimeMr14C
*/
SOpts wsopts;
int build_searchopts(aClient *, int, char **);
int chk_who(aClient *, int);
int build_searchopts(aClient *sptr, int parc, char *parv[])
{
static char *who_help[] = {
"/WHO [+|-][acghmnsu] [args]",
"Flags are specified like channel modes, the flags cgmnsu all have arguments",
"Flags are set to a positive check by +, a negative check by -",
"The flags work as follows:",
"Flag a: user is away",
"Flag c <channel>: user is on <channel>,",
" no wildcards accepted",
"Flag g <gcos/realname>: user has string <gcos> in their GCOS,",
" wildcards accepted, oper only",
"Flag h <host>: user has string <host> in their hostname,",
" wildcards accepted",
"Flag i <ip>: user is from <ip> wildcards accepted,",
"Flag f <fakehost>: user has string <fakehost> in their",
" hostname output.",
"Flag m <usermodes>: user has <usermodes> set on them,",
" only o/A/a for nonopers",
"Flag n <nick>: user has string <nick> in their nickname,",
" wildcards accepted",
"Flag s <server>: user is on server <server>,",
" wildcards not accepted",
"Flag u <user>: user has string <user> in their username,",
" wildcards accepted",
"Flag L <name>: user has string <name> as their language,",
NULL
};
char *flags, change = 1, *s;
int args = 1;
memset((char *) &wsopts, '\0', sizeof(SOpts));
/*
* if we got no extra arguments, send them the help. yeech.
* if it's /who ?, send them the help
*/
if (parc < 1 || parv[0][0] == '?') {
char **ptr = who_help;
for (; *ptr; ptr++)
send_me_numeric(sptr, RPL_COMMANDSYNTAX, *ptr);
send_me_numeric(sptr, RPL_ENDOFWHO, "?");
return 0;
}
/*
* backwards compatibility
*/
else if (parv[0][0] == '0' && parv[0][1] == 0) {
if (parc > 1 && *parv[1] == 'o') {
wsopts.check_umode = 1;
wsopts.umode_plus = 1;
wsopts.umodes = UMODE_o;
}
wsopts.host_plus = 1;
wsopts.host = "*";
return 1;
}
/*
* if the first argument isn't a list of stuff
*/
else if (parv[0][0] != '+' && parv[0][0] != '-') {
if (parv[0][0] == '#' || parv[0][0] == '&') {
wsopts.channel = find_channel(parv[0]);
if (wsopts.channel == NULL) {
send_me_numeric(sptr, ERR_NOSUCHCHANNEL, parv[0]);
return 0;
}
} else {
/*
* If the arguement has a . in it, treat it as an
* address. Otherwise treat it as a nick. -Rak
*/
if (strchr(parv[0], '.')) {
wsopts.host_plus = 1;
wsopts.host = parv[0];
} else {
wsopts.nick_plus = 1;
wsopts.nick = parv[0];
}
}
return 1;
}
/*
* now walk the list (a lot like set_mode) and set arguments
* as appropriate.
*/
flags = parv[0];
wsopts.langnum = 0;
while (*flags) {
switch (*flags) {
case '+':
case '-':
change = (*flags == '+' ? 1 : 0);
break;
case 'a':
if (change)
wsopts.away_plus = 1; /* they want here people */
else
wsopts.away_plus = 0;
wsopts.check_away = 1;
break;
case 'c':
if (parv[args] == NULL || !change) {
send_me_numeric(sptr, ERR_WHOSYNTAX);
return 0;
}
wsopts.channel = find_channel(parv[args]);
if (wsopts.channel == NULL) {
send_me_numeric(sptr, ERR_NOSUCHCHANNEL, parv[args]);
return 0;
}
wsopts.chan_plus = change;
args++;
break;
case 'f':
if (parv[args] == NULL) {
send_me_numeric(sptr, ERR_WHOSYNTAX);
return 0;
}
wsopts.fakehost = parv[args];
wsopts.fhost_plus = change;
args++;
break;
case 'g':
if (parv[args] == NULL || !IsAnOper(sptr)) {
send_me_numeric(sptr, ERR_WHOSYNTAX);
return 0;
}
wsopts.gcos = parv[args];
wsopts.gcos_plus = change;
args++;
break;
case 'h':
if (parv[args] == NULL) {
send_me_numeric(sptr, ERR_WHOSYNTAX);
return 0;
}
wsopts.host = parv[args];
wsopts.host_plus = change;
args++;
break;
case 'i':
if (parv[args] == NULL || !IsAnOper(sptr)) {
send_me_numeric(sptr, ERR_WHOSYNTAX);
return 0;
}
wsopts.ip = parv[args];
wsopts.ip_plus = change;
args++;
break;
case 'L':
if (parv[args] == NULL) {
send_me_numeric(sptr, ERR_WHOSYNTAX);
return 0;
}
wsopts.lang = parv[args];
wsopts.langnum = lang_parse(parv[args]);
wsopts.lang_plus = change;
args++;
break;
case 'm':
if (parv[args] == NULL) {
send_me_numeric(sptr, ERR_WHOSYNTAX);
return 0;
}
s = parv[args];
while (*s) {
if (umodetab[(int) *s].in_use)
wsopts.umodes |= umodetab[(int) *s].type;
s++;
}
if (!IsAnOper(sptr))
wsopts.umodes = (wsopts.umodes & (UMODE_o | UMODE_a | UMODE_A));
wsopts.umode_plus = change;
if (wsopts.umodes)
wsopts.check_umode = 1;
args++;
break;
case 'n':
if (parv[args] == NULL) {
send_me_numeric(sptr, ERR_WHOSYNTAX);
return 0;
}
wsopts.nick = parv[args];
wsopts.nick_plus = change;
args++;
break;
case 's':
if (parv[args] == NULL || !change) {
send_me_numeric(sptr, ERR_WHOSYNTAX);
return 0;
}
wsopts.server = find_server(parv[args]);
if (wsopts.server == NULL) {
send_me_numeric(sptr, ERR_NOSUCHSERVER, parv[args]);
return 0;
}
wsopts.serv_plus = change;
args++;
break;
case 'u':
if (parv[args] == NULL) {
send_me_numeric(sptr, ERR_WHOSYNTAX);
return 0;
}
wsopts.user = parv[args];
wsopts.user_plus = change;
args++;
break;
}
flags++;
}
return 1;
}
/*
* these four are used by chk_who to check gcos/nick/user/host/ip/fakehost
* respectively
*/
int (*gchkfn) (char *, char *);
int (*nchkfn) (char *, char *);
int (*uchkfn) (char *, char *);
int (*hchkfn) (char *, char *);
int (*ichkfn) (char *, char *);
int (*lchkfn) (int, int);
int (*fchkfn) (char *, char *);
int chk_who(aClient *ac, int showall)
{
if (!IsPerson(ac))
return 0;
if (IsInvisible(ac) && !showall)
return 0;
if (wsopts.check_umode)
if ((wsopts.umode_plus && !((ac->umode & wsopts.umodes) == wsopts.umodes))
|| (!wsopts.umode_plus && ((ac->umode & wsopts.umodes) == wsopts.umodes)))
return 0;
if (wsopts.check_away)
if ((wsopts.away_plus && ac->user->away == NULL) ||
(!wsopts.away_plus && ac->user->away != NULL))
return 0;
/*
* while this is wasteful now, in the future
* when clients contain pointers to their servers
* of origin, this'll become a 4 byte check instead of a irc_strcmp
* -wd
*
* welcome to the future... :)
* -lucas
*/
if (wsopts.serv_plus)
if (wsopts.server != ac->servptr)
return 0;
/*
* we only call match once, since if the first condition
* isn't true, most (all?) compilers will never try the
* second...phew :)
*/
if (wsopts.user != NULL)
if ((wsopts.user_plus && uchkfn(wsopts.user, ac->user->username))
|| (!wsopts.user_plus && !uchkfn(wsopts.user, ac->user->username)))
return 0;
if (wsopts.nick != NULL)
if ((wsopts.nick_plus && nchkfn(wsopts.nick, ac->name)) ||
(!wsopts.nick_plus && !nchkfn(wsopts.nick, ac->name)))
return 0;
if (wsopts.host != NULL)
if ((wsopts.host_plus && hchkfn(wsopts.host, ac->user->host)) ||
(!wsopts.host_plus && !hchkfn(wsopts.host, ac->user->host)))
return 0;
if (wsopts.fakehost != NULL)
if ((wsopts.fhost_plus && fchkfn(wsopts.fakehost, ac->user->fakehost))
|| (!wsopts.fhost_plus && !fchkfn(wsopts.fakehost, ac->user->fakehost)))
return 0;
if (wsopts.ip != NULL)
if ((wsopts.ip_plus && ichkfn(wsopts.ip, ac->hostip)) ||
(!wsopts.ip_plus && !ichkfn(wsopts.ip, ac->hostip)))
return 0;
if (wsopts.gcos != NULL)
if ((wsopts.gcos_plus && gchkfn(wsopts.gcos, ac->info)) ||
(!wsopts.gcos_plus && !gchkfn(wsopts.gcos, ac->info)))
return 0;
if (wsopts.langnum)
if ((wsopts.lang_plus && lchkfn(wsopts.langnum, ac->lang)) ||
(!wsopts.lang_plus && !lchkfn(wsopts.langnum, ac->lang)))
return 0;
return 1;
}
static inline char *first_visible_channel(aClient *cptr, aClient *sptr)
{
dlink_node *lp;
int secret = 0;
aChannel *chptr = NULL;
static char chnbuf[CHANNELLEN + 2];
if (cptr->user->channel.head) {
if (IsSeeHidden(sptr)) {
chptr = cptr->user->channel.head->data;
if (!(ShowChannelNames(sptr, chptr)))
secret = 1;
} else {
for (lp = cptr->user->channel.head; lp; lp = lp->next) {
if (ShowChannelNames(sptr, (aChannel *) lp->data))
break;
}
if (lp)
chptr = lp->data;
}
if (chptr) {
if (!secret)
return chptr->chname;
ircsprintf(chnbuf, "%%%s", chptr->chname);
return chnbuf;
}
}
return "*";
}
/*
* allow lusers only 200 replies from /who
*/
#define MAXWHOREPLIES 200
#define WHO_HOPCOUNT(s, a) ((IsULine((a)) && !IsAnOper((s))) ? 0 : a->hopcount)
int m_who(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
struct ChanMember *cm;
aClient *ac;
dlink_node *ptr;
int shown = 0, i = 0, showall = IsSeeHidden(sptr);
char out[HOSTLEN];
char status[4];
if (!MyClient(sptr))
return 0;
if (!build_searchopts(sptr, parc - 1, parv + 1))
return 0; /* /who was no good */
if (wsopts.gcos != NULL && (strchr(wsopts.gcos, '?')) == NULL &&
(strchr(wsopts.gcos, '*')) == NULL)
gchkfn = irc_strcmp;
else
gchkfn = match;
if (wsopts.nick != NULL && (strchr(wsopts.nick, '?')) == NULL &&
(strchr(wsopts.nick, '*')) == NULL)
nchkfn = irc_strcmp;
else
nchkfn = match;
if (wsopts.user != NULL && (strchr(wsopts.user, '?')) == NULL &&
(strchr(wsopts.user, '*')) == NULL)
uchkfn = irc_strcmp;
else
uchkfn = match;
if (wsopts.host != NULL && (strchr(wsopts.host, '?')) == NULL &&
(strchr(wsopts.host, '*')) == NULL)
hchkfn = irc_strcmp;
else
hchkfn = match;
if (wsopts.fakehost != NULL && (strchr(wsopts.fakehost, '?')) == NULL &&
(strchr(wsopts.fakehost, '*')) == NULL)
fchkfn = irc_strcmp;
else
fchkfn = match;
if (wsopts.ip != NULL && (strchr(wsopts.ip, '?')) == NULL && (strchr(wsopts.ip, '*')) == NULL)
ichkfn = irc_strcmp;
else
ichkfn = match;
if (wsopts.langnum)
lchkfn = irc_equal;
if (wsopts.channel != NULL) {
if (IsMember(sptr, wsopts.channel))
showall = 1;
else if (SecretChannel(wsopts.channel) && IsSeeHidden(sptr))
showall = 1;
else if (NamesChannel(wsopts.channel) && IsSeeHidden(sptr))
showall = 1;
else if (!SecretChannel(wsopts.channel) && !NamesChannel(wsopts.channel) && IsAnOper(sptr))
showall = 1;
else
showall = 0;
if (showall || !SecretChannel(wsopts.channel) || !NamesChannel(wsopts.channel)) {
for (ptr = wsopts.channel->members.head; ptr; ptr = ptr->next) {
cm = ptr->data;
if (!cm)
continue;
ac = cm->client_p;
i = 0;
if (!chk_who(ac, showall))
continue;
/*
* wow, they passed it all, give them the reply...
* IF they haven't reached the max, or they're an oper
*/
status[i++] = (ac->user->away == NULL ? 'H' : 'G');
status[i] = (IsAnOper(ac) ? '*' : ((IsInvisible(ac) && IsAnOper(sptr)) ? '%' : 0));
if (!IsChanHideOps(wsopts.channel) || IsSeeHidden(sptr)) {
status[((status[i]) ? ++i : i)] =
(IsChanUser(ac, wsopts.channel, CHFL_CHANOP) ? '@' :
IsChanUser(ac, wsopts.channel, CHFL_VOICE) ? '+' : 0);
}
status[++i] = 0;
send_me_numeric(sptr, RPL_WHOREPLY, wsopts.channel->chname, ac->user->username,
IsFake(ac) ? ac->user->fakehost : ac->user->host,
((WillHideName(ac->from) || (MyClient(ac) && ServerHide.enable)) ?
stealth_server(ac->user->server, out) : ac->user->server),
ac->name, status, WHO_HOPCOUNT(sptr, ac), ac->info);
}
}
send_me_numeric(sptr, RPL_ENDOFWHO, wsopts.channel->chname);
return 0;
}
/*
* if (for whatever reason) they gave us a nick with no
* wildcards, just do a find_person, bewm!
*/
else if (nchkfn == irc_strcmp) {
ac = find_person(wsopts.nick);
if (ac != NULL) {
if (!chk_who(ac, 1)) {
send_me_numeric(sptr, RPL_ENDOFWHO,
wsopts.host != NULL ? wsopts.host : wsopts.nick);
return 0;
} else {
status[0] = (ac->user->away == NULL ? 'H' : 'G');
status[1] = (IsAnOper(ac) ? '*' : ((IsInvisible(ac) && IsAnOper(sptr)) ? '%' : 0));
status[2] = 0;
send_me_numeric(sptr, RPL_WHOREPLY,
(!IsWhoisHideChan(ac)) ?
first_visible_channel(ac, sptr) : "*", ac->user->username,
IsFake(ac) ? ac->user->fakehost : ac->user->host,
((WillHideName(ac->from) || (MyClient(ac) && ServerHide.enable)) ?
stealth_server(ac->user->server, out) : ac->user->server),
ac->name, status, WHO_HOPCOUNT(sptr, ac), ac->info);
send_me_numeric(sptr, RPL_ENDOFWHO,
wsopts.host != NULL ? wsopts.host : wsopts.nick);
return 0;
}
}
send_me_numeric(sptr, RPL_ENDOFWHO, wsopts.host != NULL ? wsopts.host : wsopts.nick);
return 0;
}
for (ac = GlobalClientList; ac; ac = ac->next) {
if (!chk_who(ac, showall))
continue;
/*
* wow, they passed it all, give them the reply...
* IF they haven't reached the max, or they're an oper
*/
if (shown == MAXWHOREPLIES && !IsAnOper(sptr)) {
send_me_numeric(sptr, ERR_WHOLIMEXCEED, MAXWHOREPLIES);
break; /* break out of loop so we can send end of who */
}
status[0] = (ac->user->away == NULL ? 'H' : 'G');
status[1] = (IsAnOper(ac) ? '*' : ((IsInvisible(ac) && IsAnOper(sptr)) ? '%' : 0));
status[2] = 0;
send_me_numeric(sptr, RPL_WHOREPLY,
(!IsWhoisHideChan(ac)) ? first_visible_channel(ac,
sptr) :
"*", ac->user->username,
IsFake(ac) ? ac->user->fakehost : ac->user->host,
((WillHideName(ac->from) || (MyClient(ac) && ServerHide.enable)) ?
stealth_server(ac->user->server, out) : ac->user->server),
ac->name, status, WHO_HOPCOUNT(sptr, ac), ac->info);
shown++;
}
send_me_numeric(sptr, RPL_ENDOFWHO,
(wsopts.host != NULL ? wsopts.host :
(wsopts.nick != NULL ? wsopts.nick :
(wsopts.user != NULL ? wsopts.user :
(wsopts.gcos != NULL ? wsopts.gcos :
(wsopts.server != NULL ? wsopts.server->name :
(wsopts.fakehost != NULL ? wsopts.fakehost :
(wsopts.langnum ? wsopts.lang : "*"))))))));
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1