Skip to content

Instantly share code, notes, and snippets.

@skizzerz
Created January 8, 2018 19:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skizzerz/f6697df9140a55ccf533e2620aa0e63c to your computer and use it in GitHub Desktop.
Save skizzerz/f6697df9140a55ccf533e2620aa0e63c to your computer and use it in GitHub Desktop.

WHOX support for UnrealIRCD 4.

To install:

  1. Add m_whox.c to your src/modules/third directory
  2. Run make
  3. Run make install
  4. Add loadmodule "third/m_whox" to your conf
  5. /rehash

This adds the token WHOX to the ISUPPORT reply and allows for specifying WHOX fields in /WHO commands.

To use: /WHO [+|-][acghimnsuMRI][%tcuihsnfdlar[,querytype]] [args] [mask] OR /WHO [mask] [+|-][acghimnsuMRI][%tcuihsnfdlar[,querytype]] [args]

For a description of the flags (the parts before the %), see the built-in /WHO documentation. This module replaces the built-in /WHO command, but retains its functionality in that regard. If the % character and fields are not specified, it operates exactly like the built-in /WHO command (returning a 352 numeric for each who reply).

Optionally, a % followed by fields can be specified in order to customize the output of /WHO. If a field is not specified, it is omitted from the output. When % and fields are specified, The ordering is fixed, and is in the order listed below:

  • t: outputs the number in querytype in reply
  • c: channel
  • u: username
  • i: IP address (255.255.255.255 if the IP is unknown, for example if the target is usermode +x and you are not an oper)
  • h: host
  • s: server
  • n: nickname
  • f: status
  • d: hop count
  • l: idle time (0 for users on other servers)
  • a: services account name (0 if none)
  • r: realname
/* m_whox.c - Add WHOX support to /WHO
* 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 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 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.
*/
/* based heavily on the original m_who.c. Due to the complexity of this
* command, it's easier to override it entirely rather than attempting
* to bolt on WHOX functionality (most of the code would need to be copied
* anyway). */
#include "unrealircd.h"
#define RPL_WHOSPCRPL 354
#define MSG_WHO "WHO"
static int whox(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]);
ModuleHeader MOD_HEADER(m_whox) = {
"whox",
"1.0",
"WHOX support for /who",
"3.2-b8-1",
NULL
};
MOD_INIT(m_whox)
{
return MOD_SUCCESS;
}
MOD_LOAD(m_whox)
{
if (!CmdoverrideAdd(modinfo->handle, "WHO", whox))
return MOD_FAILED;
if (!IsupportAdd(modinfo->handle, "WHOX", NULL))
return MOD_FAILED;
return MOD_SUCCESS;
}
MOD_UNLOAD(m_whox)
{
return MOD_SUCCESS;
}
static void do_channel_who(aClient *sptr, aChannel *channel, char *mask);
static void make_who_status(aClient *, aClient *, aChannel *, Member *, char *, int);
static void do_other_who(aClient *sptr, char *mask);
static void send_who_reply(aClient *, aClient *, char *, char *, char *);
static char *first_visible_channel(aClient *, aClient *, int *);
static int parse_who_options(aClient *, int, char **);
static void who_sendhelp(aClient *);
#define WF_OPERONLY 0x01 /**< only show opers */
#define WF_ONCHANNEL 0x02 /**< we're on the channel we're /who'ing */
#define WF_WILDCARD 0x04 /**< a wildcard /who */
#define WF_REALHOST 0x08 /**< want real hostnames */
#define WF_IP 0x10 /**< want IP addresses */
static int who_flags;
#define WHO_CANTSEE 0x01 /**< set if we can't see them */
#define WHO_CANSEE 0x02 /**< set if we can */
#define WHO_OPERSEE 0x04 /**< set if we only saw them because we're an oper */
#define FVC_HIDDEN 0x01
#define WHO_WANT 1
#define WHO_DONTWANT 2
#define WHO_DONTCARE 0
struct {
int want_away;
int want_channel;
char *channel; /**< if they want one */
int want_gecos;
char *gecos;
int want_server;
char *server;
int want_host;
char *host;
int want_nick;
char *nick;
int want_user;
char *user;
int want_ip;
char *ip;
int want_port;
int port;
int want_umode;
int umodes_dontwant;
int umodes_want;
int common_channels_only;
int whox_querytype;
char whox_fields[13];
} wfl;
/** The /who command: retrieves information from users. */
static int whox(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[])
{
aChannel *target_channel;
char *mask = parv[1];
char star[] = "*";
int i = 0;
if (!MyClient(sptr))
return 0;
who_flags = 0;
memset(&wfl, 0, sizeof(wfl));
if (parc > 1)
{
i = parse_who_options(sptr, parc - 1, parv + 1);
if (i < 0)
{
sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name, mask);
return 0;
}
}
if (parc-i < 2 || strcmp(parv[1 + i], "0") == 0)
mask = star;
else
mask = parv[1 + i];
collapse(mask);
if (!i && parc > 2 && *parv[2] == 'o')
who_flags |= WF_OPERONLY;
else if (!i && parc > 2)
{
i = parse_who_options(sptr, parc - 2, parv + 2);
if (i < 0)
{
sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name, mask);
return 0;
}
}
if (*mask == '\0')
{
/* no mask given */
sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name, "*");
return 0;
}
if ((target_channel = find_channel(mask, NULL)) != NULL)
{
do_channel_who(sptr, target_channel, mask);
sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name, mask);
return 0;
}
if (wfl.channel && wfl.want_channel == WHO_WANT &&
(target_channel = find_channel(wfl.channel, NULL)) != NULL)
{
do_channel_who(sptr, target_channel, mask);
sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name, mask);
return 0;
}
else
{
do_other_who(sptr, mask);
sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name, mask);
return 0;
}
return 0;
}
static void who_sendhelp(aClient *sptr)
{
char *who_help[] = {
"/WHO [+|-][achmnsuM][%tcuihsnfdlar[,<querytype>]] [args] [mask]",
"Flags are specified like channel modes, the flags chmnsu 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 h <host>: user has string <host> in their hostname,",
" wildcards accepted",
"Flag m <usermodes>: user has <usermodes> set, only o is allowed",
"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",
"Behavior flags:",
"Flag M: check for user in channels I am a member of",
"Fields begin with the % character. If any fields are specified, the reply",
"uses the 354 numeric instead of 352. Fields not specified are omitted from",
"the reply, while those specified are outputted in the order listed here.",
"The fields work as follows:",
"Field t: outputs the number in <querytype> in reply",
"Field c: channel",
"Field u: username",
"Field i: IP address",
"Field h: host",
"Field s: server",
"Field n: nickname",
"Field f: status",
"Field d: hop count",
"Field l: idle time (0 for users on other servers)",
"Field a: services account name (0 if none)",
"Field r: realname",
NULL
};
char *who_oper_help[] = {
"/WHO [+|-][acghimnsuMRI][%tcuihsnfdlar[,<querytype>]] [args] [mask]",
"Flags are specified like channel modes, the flags chigmnsu 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",
"Flag h <host>: user has string <host> in their hostname,",
" wildcards accepted",
"Flag i <ip>: user has string <ip> in their IP address,",
" wildcards accepted",
"Flag p <port>: user is connecting on port <port>,",
" local connections only",
"Flag m <usermodes>: user has <usermodes> set",
"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",
"Behavior flags:",
"Flag M: check for user in channels I am a member of",
"Flag R: show users' real hostnames",
"Flag I: show users' IP addresses",
"Fields begin with the % character. If any fields are specified, the reply",
"uses the 354 numeric instead of 352. Fields not specified are omitted from",
"the reply, while those specified are outputted in the order listed here.",
"The fields work as follows:",
"Field t: outputs the number in <querytype> in reply",
"Field c: channel",
"Field u: username",
"Field i: IP address",
"Field h: host",
"Field s: server",
"Field n: nickname",
"Field f: status",
"Field d: hop count",
"Field l: idle time (0 for users on other servers)",
"Field a: services account name (0 if none)",
"Field r: realname",
NULL
};
char **s;
if (IsOper(sptr))
s = who_oper_help;
else
s = who_help;
for (; *s; s++)
sendto_one(sptr, getreply(RPL_LISTSYNTAX), me.name, sptr->name, *s);
}
#define WHO_ADD 1
#define WHO_DEL 2
static int parse_who_options(aClient *sptr, int argc, char **argv)
{
char *s = argv[0];
int what = WHO_ADD;
int i = 1;
/* A few helper macro's because this is used a lot, added during recode by Syzop. */
/** function requiress a parameter: check if there's one, if not: return -1. */
#define REQUIRE_PARAM() { if (i >= argc) { \
who_sendhelp(sptr); \
return -1; \
} } while(0);
/** set option 'x' depending on 'what' (add/want or del/dontwant) */
#define SET_OPTION(x) { if (what == WHO_ADD) \
x = WHO_WANT; \
else \
x = WHO_DONTWANT; \
} while(0);
/** Eat a param, set the param in memory and set the option to want or dontwant */
#define DOIT(x,y) { REQUIRE_PARAM(); x = argv[i]; SET_OPTION(y); i++; } while(0);
if (*s != '-' && *s != '+' && *s != '%')
return 0;
while (*s)
{
switch (*s)
{
case '+':
what = WHO_ADD;
break;
case '-':
what = WHO_DEL;
break;
case 'a':
SET_OPTION(wfl.want_away);
break;
case 'c':
DOIT(wfl.channel, wfl.want_channel);
break;
case 'g':
REQUIRE_PARAM()
if (!IsOper(sptr))
break; /* oper-only */
wfl.gecos = argv[i];
SET_OPTION(wfl.want_gecos);
i++;
break;
case 's':
DOIT(wfl.server, wfl.want_server);
break;
case 'h':
DOIT(wfl.host, wfl.want_host);
break;
case 'i':
REQUIRE_PARAM()
if (!IsOper(sptr))
break; /* oper-only */
wfl.ip = argv[i];
SET_OPTION(wfl.want_ip);
i++;
break;
case 'n':
DOIT(wfl.nick, wfl.want_nick);
break;
case 'u':
DOIT(wfl.user, wfl.want_user);
break;
case 'm':
REQUIRE_PARAM()
{
char *s = argv[i];
int *umodes;
if (what == WHO_ADD)
umodes = &wfl.umodes_want;
else
umodes = &wfl.umodes_dontwant;
while (*s)
{
int i;
for (i = 0; i <= Usermode_highest; i++)
if (*s == Usermode_Table[i].flag)
{
*umodes |= Usermode_Table[i].mode;
break;
}
s++;
}
if (!IsOper(sptr))
*umodes = *umodes & UMODE_OPER; /* these are usermodes regular users may search for. just oper now. */
if (*umodes == 0)
return -1;
}
i++;
break;
case 'p':
REQUIRE_PARAM()
if (!IsOper(sptr))
break; /* oper-only */
wfl.port = atoi(argv[i]);
SET_OPTION(wfl.want_port);
i++;
break;
case 'M':
SET_OPTION(wfl.common_channels_only);
break;
case 'R':
if (!IsOper(sptr))
break;
if (what == WHO_ADD)
who_flags |= WF_REALHOST;
else
who_flags &= ~WF_REALHOST;
break;
case 'I':
if (!IsOper(sptr))
break;
if (what == WHO_ADD)
who_flags |= WF_IP;
else
who_flags &= ~WF_IP;
break;
case '%':
s++;
goto process_whox;
default:
who_sendhelp(sptr);
return -1;
}
s++;
}
return i;
process_whox:
while (*s)
{
switch (*s)
{
case 't':
wfl.whox_fields[0] = 1;
wfl.whox_fields[1] = 1;
break;
case 'c':
wfl.whox_fields[0] = 1;
wfl.whox_fields[2] = 1;
break;
case 'u':
wfl.whox_fields[0] = 1;
wfl.whox_fields[3] = 1;
break;
case 'i':
wfl.whox_fields[0] = 1;
wfl.whox_fields[4] = 1;
break;
case 'h':
wfl.whox_fields[0] = 1;
wfl.whox_fields[5] = 1;
break;
case 's':
wfl.whox_fields[0] = 1;
wfl.whox_fields[6] = 1;
break;
case 'n':
wfl.whox_fields[0] = 1;
wfl.whox_fields[7] = 1;
break;
case 'f':
wfl.whox_fields[0] = 1;
wfl.whox_fields[8] = 1;
break;
case 'd':
wfl.whox_fields[0] = 1;
wfl.whox_fields[9] = 1;
break;
case 'l':
wfl.whox_fields[0] = 1;
wfl.whox_fields[10] = 1;
break;
case 'a':
wfl.whox_fields[0] = 1;
wfl.whox_fields[11] = 1;
break;
case 'r':
wfl.whox_fields[0] = 1;
wfl.whox_fields[12] = 1;
break;
case ',':
s++;
goto process_querytype;
default:
who_sendhelp(sptr);
return -1;
}
s++;
}
return i;
process_querytype:
/* s points to a string representing an int querytype (could be empty) */
wfl.whox_querytype = strtol(s, NULL, 10);
return i;
#undef REQUIRE_PARAM
#undef SET_OPTION
#undef DOIT
}
static int can_see(aClient *sptr, aClient *acptr, aChannel *channel)
{
int ret = 0;
int i=0;
Hook *h;
char has_common_chan = 0;
do {
/* can only see people */
if (!IsPerson(acptr))
return WHO_CANTSEE;
/* can only see opers if thats what they want */
if (who_flags & WF_OPERONLY)
{
if (!IsOper(acptr))
return ret | WHO_CANTSEE;
if (IsHideOper(acptr)) {
if (IsOper(sptr))
ret |= WHO_OPERSEE;
else
return ret | WHO_CANTSEE;
}
}
/* if they only want people who are away */
if ((wfl.want_away == WHO_WANT && !acptr->user->away) ||
(wfl.want_away == WHO_DONTWANT && acptr->user->away))
return WHO_CANTSEE;
/* if they only want people on a certain channel. */
if (wfl.want_channel != WHO_DONTCARE)
{
aChannel *chan = find_channel(wfl.channel, NULL);
if (!chan && wfl.want_channel == WHO_WANT)
return WHO_CANTSEE;
if ((wfl.want_channel == WHO_WANT) && !IsMember(acptr, chan))
return WHO_CANTSEE;
if ((wfl.want_channel == WHO_DONTWANT) && IsMember(acptr, chan))
return WHO_CANTSEE;
}
/* if they only want people with a certain gecos */
if (wfl.want_gecos != WHO_DONTCARE)
{
if (((wfl.want_gecos == WHO_WANT) && match(wfl.gecos, acptr->info)) ||
((wfl.want_gecos == WHO_DONTWANT) && !match(wfl.gecos, acptr->info)))
{
return WHO_CANTSEE;
}
}
/* if they only want people with a certain server */
if (wfl.want_server != WHO_DONTCARE)
{
if (((wfl.want_server == WHO_WANT) && stricmp(wfl.server, acptr->user->server)) ||
((wfl.want_server == WHO_DONTWANT) && !stricmp(wfl.server, acptr->user->server)))
{
return WHO_CANTSEE;
}
}
/* if they only want people with a certain host */
if (wfl.want_host != WHO_DONTCARE)
{
char *host;
if (IsOper(sptr))
host = acptr->user->realhost;
else
host = GetHost(acptr);
if (((wfl.want_host == WHO_WANT) && match(wfl.host, host)) ||
((wfl.want_host == WHO_DONTWANT) && !match(wfl.host, host)))
{
return WHO_CANTSEE;
}
}
/* if they only want people with a certain IP */
if (wfl.want_ip != WHO_DONTCARE)
{
char *ip;
ip = acptr->ip;
if (!ip)
return WHO_CANTSEE;
if (((wfl.want_ip == WHO_WANT) && match(wfl.ip, ip)) ||
((wfl.want_ip == WHO_DONTWANT) && !match(wfl.ip, ip)))
{
return WHO_CANTSEE;
}
}
/* if they only want people connecting on a certain port */
if (wfl.want_port != WHO_DONTCARE)
{
int port;
if (!MyClient(acptr))
return WHO_CANTSEE;
port = acptr->local->listener->port;
if (((wfl.want_port == WHO_WANT) && wfl.port != port) ||
((wfl.want_port == WHO_DONTWANT) && wfl.port == port))
{
return WHO_CANTSEE;
}
}
/* if they only want people with a certain nick.. */
if (wfl.want_nick != WHO_DONTCARE)
{
if (((wfl.want_nick == WHO_WANT) && match(wfl.nick, acptr->name)) ||
((wfl.want_nick == WHO_DONTWANT) && !match(wfl.nick, acptr->name)))
{
return WHO_CANTSEE;
}
}
/* if they only want people with a certain username */
if (wfl.want_user != WHO_DONTCARE)
{
if (((wfl.want_user == WHO_WANT) && match(wfl.user, acptr->user->username)) ||
((wfl.want_user == WHO_DONTWANT) && !match(wfl.user, acptr->user->username)))
{
return WHO_CANTSEE;
}
}
/* if they only want people with a certain umode */
if (wfl.umodes_want)
{
if (!(acptr->umodes & wfl.umodes_want) || (!IsOper(sptr) && (acptr->umodes & UMODE_HIDEOPER)))
return WHO_CANTSEE;
}
if (wfl.umodes_dontwant)
{
if ((acptr->umodes & wfl.umodes_dontwant) && (!(acptr->umodes & UMODE_HIDEOPER) || IsOper(sptr)))
return WHO_CANTSEE;
}
/* if they only want common channels */
if (wfl.common_channels_only)
{
if (!has_common_channels(sptr, acptr))
return WHO_CANTSEE;
has_common_chan = 1;
}
if (channel)
{
int member = who_flags & WF_ONCHANNEL;
if (SecretChannel(channel) || HiddenChannel(channel))
{
/* if they aren't on it.. they can't see it */
if (!(who_flags & WF_ONCHANNEL))
break;
}
if (IsInvisible(acptr) && !member)
break;
if (!user_can_see_member(sptr, acptr, channel))
continue; /* invisible (eg: due to delayjoin) */
}
else
{
/* a user/mask who */
/* If the common channel info hasn't been set, set it now */
if (!wfl.common_channels_only)
has_common_chan = has_common_channels(sptr, acptr);
if (IsInvisible(acptr) && !has_common_chan)
{
/* don't show them unless it's an exact match
or it is the user requesting the /who */
if ((who_flags & WF_WILDCARD) && sptr != acptr)
break;
}
}
/* phew.. show them. */
return WHO_CANSEE;
} while (0);
/* if we get here, it's oper-dependant. */
if (IsOper(sptr))
return ret | WHO_OPERSEE | WHO_CANSEE;
else
{
if (sptr == acptr)
return ret | WHO_CANSEE;
else
return ret | WHO_CANTSEE;
}
}
static void do_channel_who(aClient *sptr, aChannel *channel, char *mask)
{
Member *cm = channel->members;
if (IsMember(sptr, channel) || ValidatePermissionsForPath("override:see:who:onchannel",sptr,NULL,channel,NULL))
who_flags |= WF_ONCHANNEL;
for (cm = channel->members; cm; cm = cm->next)
{
aClient *acptr = cm->cptr;
char status[32];
int cansee;
if ((cansee = can_see(sptr, acptr, channel)) & WHO_CANTSEE)
continue;
make_who_status(sptr, acptr, channel, cm, status, cansee);
send_who_reply(sptr, acptr, channel->chname, status, "");
}
}
static void make_who_status(aClient *sptr, aClient *acptr, aChannel *channel,
Member *cm, char *status, int cansee)
{
int i = 0;
Hook *h;
if (acptr->user->away)
status[i++] = 'G';
else
status[i++] = 'H';
if (IsARegNick(acptr))
status[i++] = 'r';
for (h = Hooks[HOOKTYPE_WHO_STATUS]; h; h = h->next)
{
int ret = (*(h->func.intfunc))(sptr, acptr, channel, cm, status, cansee);
if (ret != 0)
status[i++] = (char)ret;
}
if (IsOper(acptr) && (!IsHideOper(acptr) || sptr == acptr || IsOper(sptr)))
status[i++] = '*';
if (IsOper(acptr) && (IsHideOper(acptr) && sptr != acptr && IsOper(sptr)))
status[i++] = '!';
if (cansee & WHO_OPERSEE)
status[i++] = '?';
if (cm)
{
if (SupportNAMESX(sptr))
{
#ifdef PREFIX_AQ
if (cm->flags & CHFL_CHANOWNER)
status[i++] = '~';
if (cm->flags & CHFL_CHANPROT)
status[i++] = '&';
#endif
if (cm->flags & CHFL_CHANOP)
status[i++] = '@';
if (cm->flags & CHFL_HALFOP)
status[i++] = '%';
if (cm->flags & CHFL_VOICE)
status[i++] = '+';
} else {
#ifdef PREFIX_AQ
if (cm->flags & CHFL_CHANOWNER)
status[i++] = '~';
else if (cm->flags & CHFL_CHANPROT)
status[i++] = '&';
else
#endif
if (cm->flags & CHFL_CHANOP)
status[i++] = '@';
else if (cm->flags & CHFL_HALFOP)
status[i++] = '%';
else if (cm->flags & CHFL_VOICE)
status[i++] = '+';
}
}
status[i] = '\0';
}
static void do_other_who(aClient *sptr, char *mask)
{
int oper = IsOper(sptr);
if (strchr(mask, '*') || strchr(mask, '?'))
{
int i = 0;
/* go through all users.. */
aClient *acptr;
who_flags |= WF_WILDCARD;
list_for_each_entry(acptr, &client_list, client_node)
{
int cansee;
char status[20];
char *channel;
int flg;
if (!IsPerson(acptr))
continue;
if (!oper) {
/* non-opers can only search on nick here */
if (match(mask, acptr->name))
continue;
} else {
/* opers can search on name, ident, virthost, ip and realhost.
* Yes, I like readable if's -- Syzop.
*/
if (!match(mask, acptr->name) || !match(mask, acptr->user->realhost) ||
!match(mask, acptr->user->username))
goto matchok;
if (IsHidden(acptr) && !match(mask, acptr->user->virthost))
goto matchok;
if (acptr->ip && !match(mask, acptr->ip))
goto matchok;
/* nothing matched... */
continue;
}
matchok:
if ((cansee = can_see(sptr, acptr, NULL)) & WHO_CANTSEE)
continue;
if (WHOLIMIT && !IsOper(sptr) && ++i > WHOLIMIT)
{
sendto_one(sptr, rpl_str(ERR_WHOLIMEXCEED), me.name, sptr->name, WHOLIMIT);
return;
}
channel = first_visible_channel(sptr, acptr, &flg);
make_who_status(sptr, acptr, NULL, NULL, status, cansee);
send_who_reply(sptr, acptr, channel, status, (flg & FVC_HIDDEN) ? "~" : "");
}
}
else
{
/* just a single client (no wildcards detected) */
aClient *acptr = find_client(mask, NULL);
int cansee;
char status[20];
char *channel;
int flg;
if (!acptr)
return;
if ((cansee = can_see(sptr, acptr, NULL)) == WHO_CANTSEE)
return;
channel = first_visible_channel(sptr, acptr, &flg);
make_who_status(sptr, acptr, NULL, NULL, status, cansee);
send_who_reply(sptr, acptr, channel, status, (flg & FVC_HIDDEN) ? "~" : "");
}
}
static void send_who_reply(aClient *sptr, aClient *acptr,
char *channel, char *status, char *xstat)
{
char *stat;
char *host;
char *ip;
char *account;
int flat = (FLAT_MAP && !IsOper(sptr)) ? 1 : 0;
char *server;
int hops;
char hopsstr[12] = {0};
char querytype[12] = {0}; /* ensure we can comfortably fit the string representation of an int + trailing null */
char idle[12] = {0};
/* helper macro for WHOX replies */
#define MAYBE_BLANK(n, x) wfl.whox_fields[n] ? " " : "", wfl.whox_fields[n] ? (x) : ""
#define MAYBE_BLANK_COLON(n, x) wfl.whox_fields[n] ? " :" : "", wfl.whox_fields[n] ? (x) : ""
stat = malloc(strlen(status) + strlen(xstat) + 1);
sprintf(stat, "%s%s", status, xstat);
if (IsOper(sptr))
{
if (who_flags & WF_REALHOST)
host = acptr->user->realhost;
else if (who_flags & WF_IP)
host = (acptr->ip ? acptr->ip : acptr->user->realhost);
else
host = GetHost(acptr);
}
else
host = GetHost(acptr);
if (!IsOper(sptr) && IsHidden(acptr))
ip = "255.255.255.255";
else
ip = (acptr->ip ? acptr->ip : "255.255.255.255");
if (IsULine(acptr) && !IsOper(sptr) && !ValidatePermissionsForPath("map:ulines",sptr,acptr,NULL,NULL) && HIDE_ULINES)
{
server = "hidden";
hops = 0;
}
else
{
server = acptr->user->server;
hops = flat ? 0 : acptr->hopcount;
}
account = (*acptr->user->svid == '*') ? "0" : acptr->user->svid;
snprintf(querytype, 12, "%d", wfl.whox_querytype);
snprintf(hopsstr, 12, "%d", hops);
/* only show idle time if they're local and aren't an oper with +I */
if (MyConnect(acptr) && (IsOper(sptr) || !(acptr->umodes & UMODE_HIDLE)))
snprintf(idle, 12, "%d", TStime() - acptr->local->last);
else
idle[0] = '0';
if (wfl.whox_fields[0])
sendto_one(sptr, ":%s 354 %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", me.name, sptr->name,
MAYBE_BLANK(1, querytype),
MAYBE_BLANK(2, channel),
MAYBE_BLANK(3, acptr->user->username),
MAYBE_BLANK(4, ip),
MAYBE_BLANK(5, host),
MAYBE_BLANK(6, server),
MAYBE_BLANK(7, acptr->name),
MAYBE_BLANK(8, stat),
MAYBE_BLANK(9, hopsstr),
MAYBE_BLANK(10, idle),
MAYBE_BLANK(11, account),
MAYBE_BLANK_COLON(12, acptr->info)
);
else
sendto_one(sptr, getreply(RPL_WHOREPLY), me.name, sptr->name,
channel, /* channel name */
acptr->user->username, /* user name */
host, /* hostname */
server, /* server name */
acptr->name, /* nick */
stat, /* status */
hops, /* hops */
acptr->info /* realname */
);
free(stat);
#undef MAYBE_BLANK
#undef MAYBE_BLANK_COLON
}
static char *first_visible_channel(aClient *sptr, aClient *acptr, int *flg)
{
Membership *lp;
*flg = 0;
for (lp = acptr->user->channel; lp; lp = lp->next)
{
aChannel *chptr = lp->chptr;
Hook *h;
int ret = EX_ALLOW;
int operoverride = 0;
int showchannel = 0;
/* Note that the code below is almost identical to the one in /WHOIS */
if (ShowChannel(sptr, chptr))
showchannel = 1;
for (h = Hooks[HOOKTYPE_SEE_CHANNEL_IN_WHOIS]; h; h = h->next)
{
int n = (*(h->func.intfunc))(sptr, acptr, chptr);
/* Hook return values:
* EX_ALLOW means 'yes is ok, as far as modules are concerned'
* EX_DENY means 'hide this channel, unless oper overriding'
* EX_ALWAYS_DENY means 'hide this channel, always'
* ... with the exception that we always show the channel if you /WHOIS yourself
*/
if (n == EX_DENY)
{
ret = EX_DENY;
}
else if (n == EX_ALWAYS_DENY)
{
ret = EX_ALWAYS_DENY;
break;
}
}
if (ret == EX_DENY)
showchannel = 0;
if (!showchannel && (ValidatePermissionsForPath("override:see:who:secret",sptr,NULL,chptr,NULL) || ValidatePermissionsForPath("override:see:whois",sptr,NULL,chptr,NULL)))
{
showchannel = 1; /* OperOverride */
operoverride = 1;
}
if ((ret == EX_ALWAYS_DENY) && (acptr != sptr))
continue; /* a module asked us to really not expose this channel, so we don't (except target==ourselves). */
if (acptr == sptr)
showchannel = 1;
if (operoverride)
*flg |= FVC_HIDDEN;
if (showchannel)
return chptr->chname;
}
/* no channels that they can see */
return "*";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment