mirror of https://github.com/pissnet/pissircd.git
664 lines
21 KiB
C
664 lines
21 KiB
C
/*
|
|
* Unreal Internet Relay Chat Daemon, src/modules/whois.c
|
|
* (C) 2000-2001 Carsten V. Munk and the UnrealIRCd Team
|
|
* (C) 2003-2021 Bram Matthys and the UnrealIRCd team
|
|
* Moved to modules by Fish (Justin Hammond) in 2001
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "unrealircd.h"
|
|
|
|
/* Structs */
|
|
ModuleHeader MOD_HEADER
|
|
= {
|
|
"whois", /* Name of module */
|
|
"5.0", /* Version */
|
|
"command /whois", /* Short description of module */
|
|
"UnrealIRCd Team",
|
|
"unrealircd-6",
|
|
};
|
|
|
|
typedef enum WhoisConfigUser {
|
|
WHOIS_CONFIG_USER_EVERYONE = 1,
|
|
WHOIS_CONFIG_USER_SELF = 2,
|
|
WHOIS_CONFIG_USER_OPER = 3,
|
|
} WhoisConfigUser;
|
|
#define HIGHEST_WHOIS_CONFIG_USER_VALUE 3 /* adjust this if you edit the enum above !! */
|
|
|
|
//this one is in include/struct.h because it needs full API exposure:
|
|
//typedef enum WhoisConfigDetails {
|
|
// ...
|
|
//} WhoisConfigDetails;
|
|
//
|
|
|
|
typedef struct WhoisConfig WhoisConfig;
|
|
struct WhoisConfig {
|
|
WhoisConfig *prev, *next;
|
|
char *name;
|
|
WhoisConfigDetails permissions[HIGHEST_WHOIS_CONFIG_USER_VALUE+1];
|
|
};
|
|
|
|
/* Global variables */
|
|
WhoisConfig *whoisconfig = NULL;
|
|
|
|
/* Forward declarations */
|
|
WhoisConfigDetails _whois_get_policy(Client *client, Client *target, const char *name);
|
|
CMD_FUNC(cmd_whois);
|
|
static int whois_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
|
|
static int whois_config_run(ConfigFile *cf, ConfigEntry *ce, int type);
|
|
static void whois_config_setdefaults(void);
|
|
|
|
MOD_TEST()
|
|
{
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
EfunctionAdd(modinfo->handle, EFUNC_WHOIS_GET_POLICY, TO_INTFUNC(_whois_get_policy));
|
|
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, whois_config_test);
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_INIT()
|
|
{
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
CommandAdd(modinfo->handle, "WHOIS", cmd_whois, MAXPARA, CMD_USER);
|
|
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, whois_config_run);
|
|
whois_config_setdefaults();
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_LOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_UNLOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
static WhoisConfig *find_whois_config(const char *name)
|
|
{
|
|
WhoisConfig *w;
|
|
for (w = whoisconfig; w; w = w->next)
|
|
if (!strcmp(w->name, name))
|
|
return w;
|
|
return NULL;
|
|
}
|
|
|
|
/* Lazy helper for whois_config_setdefaults */
|
|
static void whois_config_add(const char *name, WhoisConfigUser user, WhoisConfigDetails details)
|
|
{
|
|
WhoisConfig *w = find_whois_config(name);
|
|
|
|
if (!w)
|
|
{
|
|
/* New one */
|
|
w = safe_alloc(sizeof(WhoisConfig));
|
|
safe_strdup(w->name, name);
|
|
AddListItem(w, whoisconfig);
|
|
}
|
|
w->permissions[user] = details;
|
|
}
|
|
|
|
static void whois_config_setdefaults(void)
|
|
{
|
|
whois_config_add("basic", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("modes", WHOIS_CONFIG_USER_SELF, WHOIS_CONFIG_DETAILS_FULL);
|
|
whois_config_add("modes", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("realhost", WHOIS_CONFIG_USER_SELF, WHOIS_CONFIG_DETAILS_FULL);
|
|
whois_config_add("realhost", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("registered-nick", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("channels", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_LIMITED);
|
|
whois_config_add("channels", WHOIS_CONFIG_USER_SELF, WHOIS_CONFIG_DETAILS_FULL);
|
|
whois_config_add("channels", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("server", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("away", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("oper", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_LIMITED);
|
|
whois_config_add("oper", WHOIS_CONFIG_USER_SELF, WHOIS_CONFIG_DETAILS_FULL);
|
|
whois_config_add("oper", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("secure", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_LIMITED);
|
|
whois_config_add("secure", WHOIS_CONFIG_USER_SELF, WHOIS_CONFIG_DETAILS_FULL);
|
|
whois_config_add("secure", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("bot", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("services", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("reputation", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("security-groups", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("geo", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("certfp", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("shunned", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("account", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("swhois", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL);
|
|
|
|
whois_config_add("idle", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_LIMITED);
|
|
whois_config_add("idle", WHOIS_CONFIG_USER_SELF, WHOIS_CONFIG_DETAILS_FULL);
|
|
whois_config_add("idle", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL);
|
|
}
|
|
|
|
static void whois_free_config(void)
|
|
{
|
|
}
|
|
|
|
static WhoisConfigUser whois_config_user_strtovalue(const char *str)
|
|
{
|
|
if (!strcmp(str, "everyone"))
|
|
return WHOIS_CONFIG_USER_EVERYONE;
|
|
if (!strcmp(str, "self"))
|
|
return WHOIS_CONFIG_USER_SELF;
|
|
if (!strcmp(str, "oper"))
|
|
return WHOIS_CONFIG_USER_OPER;
|
|
return 0;
|
|
}
|
|
|
|
static WhoisConfigDetails whois_config_details_strtovalue(const char *str)
|
|
{
|
|
if (!strcmp(str, "full"))
|
|
return WHOIS_CONFIG_DETAILS_FULL;
|
|
if (!strcmp(str, "limited"))
|
|
return WHOIS_CONFIG_DETAILS_LIMITED;
|
|
if (!strcmp(str, "none"))
|
|
return WHOIS_CONFIG_DETAILS_NONE;
|
|
return 0;
|
|
}
|
|
|
|
static int whois_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
|
{
|
|
int errors = 0;
|
|
ConfigEntry *cep, *cepp;
|
|
|
|
if (type != CONFIG_SET)
|
|
return 0;
|
|
|
|
/* We are only interrested in set::whois-details.. */
|
|
if (!ce || strcmp(ce->name, "whois-details"))
|
|
return 0;
|
|
|
|
for (cep = ce->items; cep; cep = cep->next)
|
|
{
|
|
if (cep->value)
|
|
{
|
|
config_error("%s:%i: set::whois-details::%s item has a value, which is unexpected. Check your syntax!",
|
|
cep->file->filename, cep->line_number, cep->name);
|
|
errors++;
|
|
continue;
|
|
}
|
|
for (cepp = cep->items; cepp; cepp = cepp->next)
|
|
{
|
|
if (!whois_config_user_strtovalue(cepp->name))
|
|
{
|
|
config_error("%s:%i: set::whois-details::%s contains unknown user category called '%s', must be one of: everyone, self, ircop",
|
|
cepp->file->filename, cepp->line_number, cep->name, cepp->name);
|
|
errors++;
|
|
continue;
|
|
} else
|
|
if (!cepp->value || !whois_config_details_strtovalue(cepp->value))
|
|
{
|
|
config_error("%s:%i: set::whois-details::%s contains unknown details type '%s', must be one of: full, limited, none",
|
|
cepp->file->filename, cepp->line_number, cep->name, cepp->name);
|
|
errors++;
|
|
continue;
|
|
} /* else it is good */
|
|
}
|
|
}
|
|
|
|
*errs = errors;
|
|
return errors ? -1 : 1;
|
|
}
|
|
|
|
static int whois_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
|
|
{
|
|
ConfigEntry *cep, *cepp;
|
|
|
|
if (type != CONFIG_SET)
|
|
return 0;
|
|
|
|
/* We are only interrested in set::whois-details.. */
|
|
if (!ce || strcmp(ce->name, "whois-details"))
|
|
return 0;
|
|
|
|
for (cep = ce->items; cep; cep = cep->next)
|
|
{
|
|
WhoisConfig *w = find_whois_config(cep->name);
|
|
if (!w)
|
|
{
|
|
/* New one */
|
|
w = safe_alloc(sizeof(WhoisConfig));
|
|
safe_strdup(w->name, cep->name);
|
|
AddListItem(w, whoisconfig);
|
|
}
|
|
for (cepp = cep->items; cepp; cepp = cepp->next)
|
|
{
|
|
WhoisConfigUser user = whois_config_user_strtovalue(cepp->name);
|
|
WhoisConfigDetails details = whois_config_details_strtovalue(cepp->value);
|
|
w->permissions[user] = details;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/** Get set::whois-details policy for an item.
|
|
* @param client The client doing the /WHOIS
|
|
* @param target The client being whoised, so the one to show all details for
|
|
* @param name The name of the whois item (eg "modes")
|
|
*/
|
|
WhoisConfigDetails _whois_get_policy(Client *client, Client *target, const char *name)
|
|
{
|
|
WhoisConfig *w = find_whois_config(name);
|
|
if (!w)
|
|
return WHOIS_CONFIG_DETAILS_DEFAULT;
|
|
if ((client == target) && (w->permissions[WHOIS_CONFIG_USER_SELF] > 0))
|
|
return w->permissions[WHOIS_CONFIG_USER_SELF];
|
|
if (IsOper(client) && (w->permissions[WHOIS_CONFIG_USER_OPER] > 0))
|
|
return w->permissions[WHOIS_CONFIG_USER_OPER];
|
|
if (w->permissions[WHOIS_CONFIG_USER_EVERYONE] > 0)
|
|
return w->permissions[WHOIS_CONFIG_USER_EVERYONE];
|
|
return WHOIS_CONFIG_DETAILS_NONE;
|
|
}
|
|
|
|
/* WHOIS command.
|
|
* parv[1] = list of nicks (comma separated)
|
|
*/
|
|
CMD_FUNC(cmd_whois)
|
|
{
|
|
Membership *lp;
|
|
Client *target;
|
|
Channel *channel;
|
|
char *nick, *tmp;
|
|
char *p = NULL;
|
|
int len, mlen;
|
|
char querybuf[BUFSIZE];
|
|
char buf[BUFSIZE];
|
|
int ntargets = 0;
|
|
int maxtargets = max_targets_for_command("WHOIS");
|
|
|
|
if (parc < 2)
|
|
{
|
|
sendnumeric(client, ERR_NONICKNAMEGIVEN);
|
|
return;
|
|
}
|
|
|
|
if (parc > 2)
|
|
{
|
|
if (hunt_server(client, recv_mtags, "WHOIS", 1, parc, parv) != HUNTED_ISME)
|
|
return;
|
|
parv[1] = parv[2];
|
|
}
|
|
|
|
strlcpy(querybuf, parv[1], sizeof(querybuf));
|
|
|
|
for (tmp = canonize(parv[1]); (nick = strtoken(&p, tmp, ",")); tmp = NULL)
|
|
{
|
|
unsigned char showchannel, wilds, hideoper; /* <- these are all boolean-alike */
|
|
NameValuePrioList *list = NULL, *e;
|
|
int policy; /* for temporary stuff */
|
|
|
|
if (MyUser(client) && (++ntargets > maxtargets))
|
|
{
|
|
sendnumeric(client, ERR_TOOMANYTARGETS, nick, maxtargets, "WHOIS");
|
|
break;
|
|
}
|
|
|
|
/* We do not support "WHOIS *" */
|
|
wilds = (strchr(nick, '?') || strchr(nick, '*'));
|
|
if (wilds)
|
|
continue;
|
|
|
|
target = find_user(nick, NULL);
|
|
if (!target)
|
|
{
|
|
sendnumeric(client, ERR_NOSUCHNICK, nick);
|
|
continue;
|
|
}
|
|
|
|
/* Ok, from this point we are going to proceed with the WHOIS.
|
|
* The idea here is NOT to send any lines, so don't call sendto functions.
|
|
* Instead, use add_nvplist_numeric() and add_nvplist_numeric_fmt()
|
|
* to add items to the whois list.
|
|
* Then at the end of this loop we call modules who can also add/remove
|
|
* whois lines, and only after that we FINALLY send all the whois lines
|
|
* in one go.
|
|
*/
|
|
|
|
hideoper = 0;
|
|
if (IsHideOper(target) && (target != client) && !IsOper(client))
|
|
hideoper = 1;
|
|
|
|
if (whois_get_policy(client, target, "basic") > WHOIS_CONFIG_DETAILS_NONE)
|
|
{
|
|
add_nvplist_numeric(&list, -1000000, "basic", client, RPL_WHOISUSER, target->name,
|
|
target->user->username,
|
|
IsHidden(target) ? target->user->virthost : target->user->realhost,
|
|
target->info);
|
|
}
|
|
|
|
if (whois_get_policy(client, target, "modes") > WHOIS_CONFIG_DETAILS_NONE)
|
|
{
|
|
add_nvplist_numeric(&list, -100000, "modes", client, RPL_WHOISMODES, target->name,
|
|
get_usermode_string(target), target->user->snomask ? target->user->snomask : "");
|
|
}
|
|
if (whois_get_policy(client, target, "realhost") > WHOIS_CONFIG_DETAILS_NONE)
|
|
{
|
|
add_nvplist_numeric(&list, -90000, "realhost", client, RPL_WHOISHOST, target->name,
|
|
(MyConnect(target) && strcmp(target->ident, "unknown")) ? target->ident : "*",
|
|
target->user->realhost, target->ip ? target->ip : "");
|
|
}
|
|
|
|
if (IsRegNick(target) && (whois_get_policy(client, target, "registered-nick") > WHOIS_CONFIG_DETAILS_NONE))
|
|
{
|
|
add_nvplist_numeric(&list, -80000, "registered-nick", client, RPL_WHOISREGNICK, target->name);
|
|
}
|
|
|
|
/* The following code deals with channels */
|
|
policy = whois_get_policy(client, target, "channels");
|
|
if (policy > WHOIS_CONFIG_DETAILS_NONE)
|
|
{
|
|
int channel_whois_lines = 0;
|
|
mlen = strlen(me.name) + strlen(client->name) + 10 + strlen(target->name);
|
|
for (len = 0, *buf = '\0', lp = target->user->channel; lp; lp = lp->next)
|
|
{
|
|
Hook *h;
|
|
int ret = EX_ALLOW;
|
|
int operoverride = 0;
|
|
|
|
channel = lp->channel;
|
|
showchannel = 0;
|
|
|
|
if (ShowChannel(client, channel))
|
|
showchannel = 1;
|
|
|
|
for (h = Hooks[HOOKTYPE_SEE_CHANNEL_IN_WHOIS]; h; h = h->next)
|
|
{
|
|
int n = (*(h->func.intfunc))(client, target, channel);
|
|
/* 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 the channel is normally hidden, but the user is an IRCOp,
|
|
* and has the channel:see:whois privilege,
|
|
* and set::whois-details for 'channels' has 'oper full',
|
|
* then show it:
|
|
*/
|
|
if (!showchannel && (ValidatePermissionsForPath("channel:see:whois",client,NULL,channel,NULL)) && (policy == WHOIS_CONFIG_DETAILS_FULL))
|
|
{
|
|
showchannel = 1; /* OperOverride */
|
|
operoverride = 1;
|
|
}
|
|
|
|
if ((ret == EX_ALWAYS_DENY) && (target != client))
|
|
continue; /* a module asked us to really not expose this channel, so we don't (except target==ourselves). */
|
|
|
|
/* This deals with target==client but also for unusual set::whois-details overrides
|
|
* such as 'everyone full'
|
|
*/
|
|
if (policy == WHOIS_CONFIG_DETAILS_FULL)
|
|
showchannel = 1;
|
|
|
|
if (showchannel)
|
|
{
|
|
if (len + strlen(channel->name) > (size_t)BUFSIZE - 4 - mlen)
|
|
{
|
|
add_nvplist_numeric_fmt(&list, -70500-channel_whois_lines, "channels", client, RPL_WHOISCHANNELS,
|
|
"%s :%s", target->name, buf);
|
|
channel_whois_lines++;
|
|
*buf = '\0';
|
|
len = 0;
|
|
}
|
|
|
|
if (operoverride)
|
|
{
|
|
/* '?' and '!' both mean we can see the channel in /WHOIS and normally wouldn't,
|
|
* but there's still a slight difference between the two...
|
|
*/
|
|
if (!PubChannel(channel))
|
|
{
|
|
/* '?' means it's a secret/private channel (too) */
|
|
*(buf + len++) = '?';
|
|
}
|
|
else
|
|
{
|
|
/* public channel but hidden in WHOIS (umode +p, service bot, etc) */
|
|
*(buf + len++) = '!';
|
|
}
|
|
}
|
|
|
|
if (!MyUser(client) || !HasCapability(client, "multi-prefix"))
|
|
{
|
|
/* Standard NAMES reply (single character) */
|
|
char c = mode_to_prefix(*lp->member_modes);
|
|
if (c)
|
|
*(buf + len++) = c;
|
|
}
|
|
else
|
|
{
|
|
/* NAMES reply with all rights included (multi-prefix / NAMESX) */
|
|
strcpy(buf + len, modes_to_prefix(lp->member_modes));
|
|
len += strlen(buf + len);
|
|
}
|
|
if (len)
|
|
*(buf + len) = '\0';
|
|
strcpy(buf + len, channel->name);
|
|
len += strlen(channel->name);
|
|
strcat(buf + len, " ");
|
|
len++;
|
|
}
|
|
}
|
|
|
|
if (buf[0] != '\0')
|
|
{
|
|
add_nvplist_numeric_fmt(&list, -70500-channel_whois_lines, "channels", client, RPL_WHOISCHANNELS,
|
|
"%s :%s", target->name, buf);
|
|
channel_whois_lines++;
|
|
}
|
|
}
|
|
|
|
if (!(IsULine(target) && !IsOper(client) && HIDE_ULINES) &&
|
|
whois_get_policy(client, target, "server") > WHOIS_CONFIG_DETAILS_NONE)
|
|
{
|
|
add_nvplist_numeric(&list, -60000, "server", client, RPL_WHOISSERVER,
|
|
target->name, target->user->server, target->uplink->info);
|
|
}
|
|
|
|
if (target->user->away && (whois_get_policy(client, target, "away") > WHOIS_CONFIG_DETAILS_NONE))
|
|
{
|
|
add_nvplist_numeric(&list, -50000, "away", client, RPL_AWAY,
|
|
target->name, target->user->away);
|
|
}
|
|
|
|
if (IsOper(target) && !hideoper)
|
|
{
|
|
policy = whois_get_policy(client, target, "oper");
|
|
if (policy == WHOIS_CONFIG_DETAILS_FULL)
|
|
{
|
|
const char *operlogin = get_operlogin(target);
|
|
const char *operclass = get_operclass(target);
|
|
|
|
if (operlogin && operclass)
|
|
{
|
|
add_nvplist_numeric_fmt(&list, -40000, "oper", client, RPL_WHOISOPERATOR,
|
|
"%s :is %s (%s) [%s]",
|
|
target->name, "an IRC Operator", operlogin, operclass);
|
|
} else
|
|
if (operlogin)
|
|
{
|
|
add_nvplist_numeric_fmt(&list, -40000, "oper", client, RPL_WHOISOPERATOR,
|
|
"%s :is %s (%s)",
|
|
target->name, "an IRC Operator", operlogin);
|
|
} else
|
|
{
|
|
add_nvplist_numeric(&list, -40000, "oper", client, RPL_WHOISOPERATOR,
|
|
target->name, "an IRC Operator");
|
|
}
|
|
} else
|
|
if (policy == WHOIS_CONFIG_DETAILS_LIMITED)
|
|
{
|
|
add_nvplist_numeric(&list, -40000, "oper", client, RPL_WHOISOPERATOR,
|
|
target->name, "an IRC Operator");
|
|
}
|
|
}
|
|
|
|
if (target->umodes & UMODE_SECURE)
|
|
{
|
|
policy = whois_get_policy(client, target, "secure");
|
|
if (policy == WHOIS_CONFIG_DETAILS_LIMITED)
|
|
{
|
|
add_nvplist_numeric(&list, -30000, "secure", client, RPL_WHOISSECURE,
|
|
target->name, "is using a Secure Connection");
|
|
} else
|
|
if (policy == WHOIS_CONFIG_DETAILS_FULL)
|
|
{
|
|
const char *ciphers = tls_get_cipher(target);
|
|
if (ciphers)
|
|
{
|
|
add_nvplist_numeric_fmt(&list, -30000, "secure", client, RPL_WHOISSECURE,
|
|
"%s :is using a Secure Connection [%s]",
|
|
target->name, ciphers);
|
|
} else {
|
|
add_nvplist_numeric(&list, -30000, "secure", client, RPL_WHOISSECURE,
|
|
target->name, "is using a Secure Connection");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* The following code deals with security-groups */
|
|
policy = whois_get_policy(client, target, "security-groups");
|
|
if ((policy > WHOIS_CONFIG_DETAILS_NONE) && !IsULine(target))
|
|
{
|
|
SecurityGroup *s;
|
|
int security_groups_whois_lines = 0;
|
|
|
|
mlen = strlen(me.name) + strlen(client->name) + 10 + strlen(target->name) + strlen("is in security-groups: ");
|
|
|
|
if (user_allowed_by_security_group_name(target, "known-users"))
|
|
strlcpy(buf, "known-users,", sizeof(buf));
|
|
else
|
|
strlcpy(buf, "unknown-users,", sizeof(buf));
|
|
len = strlen(buf);
|
|
|
|
for (s = securitygroups; s; s = s->next)
|
|
{
|
|
if (len + strlen(s->name) > (size_t)BUFSIZE - 4 - mlen)
|
|
{
|
|
buf[len-1] = '\0';
|
|
add_nvplist_numeric_fmt(&list, -15000-security_groups_whois_lines, "security-groups",
|
|
target, RPL_WHOISSPECIAL,
|
|
"%s :is in security-groups: %s", target->name, buf);
|
|
security_groups_whois_lines++;
|
|
*buf = '\0';
|
|
len = 0;
|
|
}
|
|
if (strcmp(s->name, "known-users") && user_allowed_by_security_group(target, s))
|
|
{
|
|
strcpy(buf + len, s->name);
|
|
len += strlen(buf+len);
|
|
strcpy(buf + len, ",");
|
|
len++;
|
|
}
|
|
}
|
|
|
|
if (*buf)
|
|
{
|
|
buf[len-1] = '\0';
|
|
add_nvplist_numeric_fmt(&list, -15000-security_groups_whois_lines, "security-groups",
|
|
client, RPL_WHOISSPECIAL,
|
|
"%s :is in security-groups: %s", target->name, buf);
|
|
security_groups_whois_lines++;
|
|
}
|
|
}
|
|
if (MyUser(target) && IsShunned(target) && (whois_get_policy(client, target, "shunned") > WHOIS_CONFIG_DETAILS_NONE))
|
|
{
|
|
add_nvplist_numeric(&list, -20000, "shunned", client, RPL_WHOISSPECIAL,
|
|
target->name, "is shunned");
|
|
}
|
|
|
|
if (target->user->swhois && (whois_get_policy(client, target, "swhois") > WHOIS_CONFIG_DETAILS_NONE))
|
|
{
|
|
SWhois *s;
|
|
int swhois_lines = 0;
|
|
|
|
for (s = target->user->swhois; s; s = s->next)
|
|
{
|
|
if (hideoper && !IsOper(client) && s->setby && !strcmp(s->setby, "oper"))
|
|
continue; /* hide oper-based swhois entries */
|
|
add_nvplist_numeric(&list, 100000+swhois_lines, "swhois", client, RPL_WHOISSPECIAL,
|
|
target->name, s->line);
|
|
swhois_lines++;
|
|
}
|
|
}
|
|
|
|
/* TODO: hmm.. this should be a bit more towards the beginning of the whois, no ? */
|
|
if (IsLoggedIn(target) && (whois_get_policy(client, target, "account") > WHOIS_CONFIG_DETAILS_NONE))
|
|
{
|
|
add_nvplist_numeric(&list, 200000, "account", client, RPL_WHOISLOGGEDIN,
|
|
target->name, target->user->account);
|
|
}
|
|
|
|
if (MyConnect(target))
|
|
{
|
|
policy = whois_get_policy(client, target, "idle");
|
|
/* If the policy is 'full' then show the idle time.
|
|
* If the policy is 'limited then show the idle time according to the +I rules
|
|
*/
|
|
if ((policy == WHOIS_CONFIG_DETAILS_FULL) ||
|
|
((policy == WHOIS_CONFIG_DETAILS_LIMITED) && !hide_idle_time(client, target)))
|
|
{
|
|
add_nvplist_numeric(&list, 500000, "idle", client, RPL_WHOISIDLE,
|
|
target->name,
|
|
(long long)(TStime() - target->local->idle_since),
|
|
(long long)target->local->creationtime);
|
|
}
|
|
}
|
|
|
|
RunHook(HOOKTYPE_WHOIS, client, target, &list);
|
|
|
|
for (e = list; e; e = e->next)
|
|
sendto_one(client, NULL, "%s", e->value);
|
|
|
|
free_nvplist(list);
|
|
}
|
|
sendnumeric(client, RPL_ENDOFWHOIS, querybuf);
|
|
}
|