632 lines
16 KiB
C
632 lines
16 KiB
C
/*
|
|
* NeoIRCd: NeoStats Group. Based on Hybird7
|
|
* m_whois.c: Shows who a user is.
|
|
*
|
|
* Copyright (C) 2002 by the past and present ircd coders, and others.
|
|
*
|
|
* 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
* USA
|
|
*
|
|
* $Id: m_whois.c,v 1.12 2003/03/06 14:01:47 fishwaldo Exp $
|
|
*/
|
|
|
|
#include "stdinc.h"
|
|
#include "tools.h"
|
|
#include "common.h"
|
|
#include "handlers.h"
|
|
#include "client.h"
|
|
#include "hash.h"
|
|
#include "channel.h"
|
|
#include "channel_mode.h"
|
|
#include "hash.h"
|
|
#include "ircd.h"
|
|
#include "numeric.h"
|
|
#include "s_conf.h"
|
|
#include "s_serv.h"
|
|
#include "s_user.h"
|
|
#include "send.h"
|
|
#include "list.h"
|
|
#include "irc_string.h"
|
|
#include "s_conf.h"
|
|
#include "msg.h"
|
|
#include "parse.h"
|
|
#include "modules.h"
|
|
#include "hook.h"
|
|
|
|
static int do_whois(struct Client *client_p, struct Client *source_p,
|
|
int parc, char *parv[]);
|
|
static int single_whois(struct Client *source_p, struct Client *target_p,
|
|
int wilds, int glob);
|
|
static void whois_person(struct Client *source_p,struct Client *target_p,int glob);
|
|
static int global_whois(struct Client *source_p, char *nick, int wilds, int glob);
|
|
|
|
static void m_whois(struct Client*, struct Client*, int, char**);
|
|
static void ms_whois(struct Client*, struct Client*, int, char**);
|
|
static void mo_whois(struct Client*, struct Client*, int, char**);
|
|
|
|
struct Message whois_msgtab = {
|
|
"WHOIS", 0, 0, 0, 0, MFLG_SLOW, 0L,
|
|
{m_unregistered, m_whois, ms_whois, mo_whois}
|
|
};
|
|
|
|
#ifndef STATIC_MODULES
|
|
void
|
|
_modinit(void)
|
|
{
|
|
hook_add_event("doing_whois");
|
|
mod_add_cmd(&whois_msgtab);
|
|
}
|
|
|
|
void
|
|
_moddeinit(void)
|
|
{
|
|
hook_del_event("doing_whois");
|
|
mod_del_cmd(&whois_msgtab);
|
|
}
|
|
|
|
const char *_version = "$Revision: 1.12 $";
|
|
#endif
|
|
/*
|
|
** m_whois
|
|
** parv[0] = sender prefix
|
|
** parv[1] = nickname masklist
|
|
*/
|
|
static void
|
|
m_whois(struct Client *client_p, struct Client *source_p,
|
|
int parc, char *parv[])
|
|
{
|
|
static time_t last_used = 0;
|
|
|
|
if (parc < 2 || BadPtr(parv[1]))
|
|
{
|
|
sendto_one(source_p, form_str(ERR_NONICKNAMEGIVEN),
|
|
me.name, parv[0]);
|
|
return;
|
|
}
|
|
|
|
if(parc > 2)
|
|
{
|
|
/* seeing as this is going across servers, we should limit it */
|
|
if((last_used + ConfigFileEntry.pace_wait_simple) > CurrentTime)
|
|
{
|
|
if(MyClient(source_p))
|
|
sendto_one(source_p,form_str(RPL_LOAD2HI),me.name,source_p->name);
|
|
return;
|
|
}
|
|
else
|
|
last_used = CurrentTime;
|
|
|
|
/* if we have serverhide enabled, they can either ask the clients
|
|
* server, or our server.. I dont see why they would need to ask
|
|
* anything else for info about the client.. --fl_
|
|
*/
|
|
if(ConfigServerHide.disable_remote)
|
|
parv[1] = parv[2];
|
|
|
|
if (hunt_server(client_p,source_p,":%s WHOIS %s :%s", 1, parc, parv) !=
|
|
HUNTED_ISME)
|
|
{
|
|
return;
|
|
}
|
|
parv[1] = parv[2];
|
|
|
|
}
|
|
do_whois(client_p,source_p,parc,parv);
|
|
}
|
|
|
|
/*
|
|
** mo_whois
|
|
** parv[0] = sender prefix
|
|
** parv[1] = nickname masklist
|
|
*/
|
|
static void
|
|
mo_whois(struct Client *client_p, struct Client *source_p,
|
|
int parc, char *parv[])
|
|
{
|
|
/* XXX Need BadPtr */
|
|
if (parc < 2 || BadPtr(parv[1]))
|
|
{
|
|
sendto_one(source_p, form_str(ERR_NONICKNAMEGIVEN),
|
|
me.name, parv[0]);
|
|
return;
|
|
}
|
|
|
|
if(parc > 2)
|
|
{
|
|
if (hunt_server(client_p,source_p,":%s WHOIS %s :%s", 1, parc, parv) !=
|
|
HUNTED_ISME)
|
|
{
|
|
return;
|
|
}
|
|
parv[1] = parv[2];
|
|
}
|
|
|
|
do_whois(client_p,source_p,parc,parv);
|
|
}
|
|
|
|
|
|
/* do_whois
|
|
*
|
|
* inputs - pointer to
|
|
* output -
|
|
* side effects -
|
|
*/
|
|
static int
|
|
do_whois(struct Client *client_p, struct Client *source_p,
|
|
int parc, char *parv[])
|
|
{
|
|
struct Client *target_p;
|
|
char *nick;
|
|
char *p = NULL;
|
|
int found=NO;
|
|
int wilds;
|
|
int glob=0;
|
|
|
|
/* This lets us make all "whois nick" queries look the same, and all
|
|
* "whois nick nick" queries look the same. We have to pass it all
|
|
* the way down to whois_person() though -- fl */
|
|
|
|
if(parc > 2)
|
|
glob = 1;
|
|
|
|
nick = parv[1];
|
|
while (*nick == ',')
|
|
nick++;
|
|
if ((p = strchr(nick,',')) != NULL)
|
|
*p = '\0';
|
|
|
|
if (*nick == '\0')
|
|
return(0);
|
|
|
|
(void)collapse(nick);
|
|
wilds = (strchr(nick, '?') || strchr(nick, '*'));
|
|
|
|
if(!wilds)
|
|
{
|
|
if((target_p = find_client(nick)) != NULL)
|
|
{
|
|
/* im being asked to reply to a client that isnt mine..
|
|
* I cant answer authoritively, so better make it non-detailed
|
|
*/
|
|
if(!MyClient(target_p))
|
|
glob=0;
|
|
|
|
if (IsServer(client_p))
|
|
client_burst_if_needed(client_p,target_p);
|
|
|
|
if(IsPerson(target_p))
|
|
{
|
|
(void)single_whois(source_p,target_p,wilds,glob);
|
|
found = YES;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!ServerInfo.hub && uplink && IsCapable(uplink,CAP_LL))
|
|
{
|
|
if(glob)
|
|
sendto_one(uplink,":%s WHOIS %s :%s",
|
|
source_p->name, nick, nick);
|
|
else
|
|
sendto_one(uplink,":%s WHOIS %s",
|
|
source_p->name, nick);
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
else /* wilds is true */
|
|
{
|
|
/* disallow wild card whois on lazylink leafs for now */
|
|
|
|
if (!ServerInfo.hub && uplink && IsCapable(uplink,CAP_LL))
|
|
{
|
|
return (0);
|
|
}
|
|
/* Oh-oh wilds is true so have to do it the hard expensive way */
|
|
found = global_whois(source_p,nick,wilds,glob);
|
|
}
|
|
|
|
if (!found)
|
|
sendto_one(source_p, form_str(ERR_NOSUCHNICK), me.name, parv[0], nick);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* global_whois()
|
|
*
|
|
* Inputs - source_p client to report to
|
|
* - target_p client to report on
|
|
* - wilds whether wildchar char or not
|
|
* Output - if found return 1
|
|
* Side Effects - do a single whois on given client
|
|
* writing results to source_p
|
|
*/
|
|
static int
|
|
global_whois(struct Client *source_p, char *nick, int wilds, int glob)
|
|
{
|
|
struct Client *target_p;
|
|
int found = NO;
|
|
|
|
for (target_p = GlobalClientList; (target_p = next_client(target_p, nick));
|
|
target_p = target_p->next)
|
|
{
|
|
if (IsServer(target_p))
|
|
continue;
|
|
/*
|
|
* I'm always last :-) and target_p->next == NULL!!
|
|
*/
|
|
if (IsMe(target_p))
|
|
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 and wildcards are
|
|
* in use (allow exact matches only);
|
|
*
|
|
* - only send replies about common or public channels
|
|
* the target user(s) are on;
|
|
*/
|
|
|
|
if(!IsRegistered(target_p))
|
|
continue;
|
|
|
|
if(single_whois(source_p, target_p, wilds, glob))
|
|
found = (YES);
|
|
}
|
|
|
|
return (found);
|
|
}
|
|
|
|
/*
|
|
* single_whois()
|
|
*
|
|
* Inputs - source_p client to report to
|
|
* - target_p client to report on
|
|
* - wilds whether wildchar char or not
|
|
* Output - if found return 1
|
|
* Side Effects - do a single whois on given client
|
|
* writing results to source_p
|
|
*/
|
|
static int
|
|
single_whois(struct Client *source_p,struct Client *target_p,
|
|
int wilds, int glob)
|
|
{
|
|
dlink_node *ptr;
|
|
struct Channel *chptr;
|
|
char *name;
|
|
int invis;
|
|
int member;
|
|
int showperson;
|
|
|
|
if (target_p->name[0] == '\0')
|
|
name = "?";
|
|
else
|
|
name = target_p->name;
|
|
|
|
if( target_p->user == NULL )
|
|
{
|
|
sendto_one(source_p, form_str(RPL_WHOISUSER), me.name,
|
|
source_p->name, name,
|
|
target_p->username, target_p->vhost, target_p->info);
|
|
sendto_one(source_p, form_str(RPL_WHOISSERVER),
|
|
me.name, source_p->name, name, "<Unknown>",
|
|
"*Not On This Net*");
|
|
return 0;
|
|
}
|
|
|
|
invis = IsInvisible(target_p);
|
|
member = (target_p->user->channel.head) ? 1 : 0;
|
|
showperson = (wilds && !invis && !member) || !wilds;
|
|
|
|
DLINK_FOREACH(ptr, target_p->user->channel.head)
|
|
{
|
|
chptr = ptr->data;
|
|
member = IsMember(source_p, chptr);
|
|
if (invis && !member)
|
|
continue;
|
|
if (member || (!invis && PubChannel(chptr)))
|
|
{
|
|
showperson = 1;
|
|
break;
|
|
}
|
|
if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
|
|
{
|
|
showperson = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(showperson)
|
|
whois_person(source_p,target_p,glob);
|
|
return (YES);
|
|
}
|
|
|
|
/*
|
|
* whois_person()
|
|
*
|
|
* Inputs - source_p client to report to
|
|
* - target_p client to report on
|
|
* Output - NONE
|
|
* Side Effects -
|
|
*/
|
|
static void
|
|
whois_person(struct Client *source_p,struct Client *target_p, int glob)
|
|
{
|
|
char buf[BUFSIZE];
|
|
char *chname;
|
|
char *server_name;
|
|
dlink_node *lp;
|
|
struct Client *a2client_p;
|
|
struct Channel *chptr;
|
|
int cur_len = 0;
|
|
int mlen;
|
|
char *t;
|
|
int tlen;
|
|
int reply_to_send = NO;
|
|
struct hook_mfunc_data hd;
|
|
char ubuf[BUFSIZE];
|
|
|
|
a2client_p = find_server(target_p->user->server);
|
|
|
|
sendto_one(source_p, form_str(RPL_WHOISUSER), me.name,
|
|
source_p->name, target_p->name,
|
|
target_p->username, target_p->vhost, target_p->info);
|
|
server_name = (char *)target_p->user->server;
|
|
|
|
ircsprintf(buf, form_str(RPL_WHOISCHANNELS),
|
|
me.name, source_p->name, target_p->name, "");
|
|
|
|
mlen = strlen(buf);
|
|
cur_len = mlen;
|
|
t = buf + mlen;
|
|
|
|
DLINK_FOREACH(lp, target_p->user->channel.head)
|
|
{
|
|
chptr = lp->data;
|
|
chname = chptr->chname;
|
|
|
|
if (ShowChannel(source_p, chptr))
|
|
{
|
|
if ((cur_len + strlen(chname) + 2) > (BUFSIZE - 4))
|
|
{
|
|
sendto_one(source_p, "%s", buf);
|
|
cur_len = mlen;
|
|
t = buf + mlen;
|
|
}
|
|
|
|
if (chptr->mode.mode & MODE_HIDEOPS && !is_any_op(chptr,source_p))
|
|
{
|
|
ircsprintf(t,"%s ",chname);
|
|
}
|
|
else
|
|
{
|
|
ircsprintf(t,"%s%s ", channel_chanop_or_voice(chptr,target_p), chname);
|
|
}
|
|
|
|
tlen = strlen(t);
|
|
t += tlen;
|
|
cur_len += tlen;
|
|
reply_to_send = YES;
|
|
}
|
|
}
|
|
|
|
if (reply_to_send)
|
|
sendto_one(source_p, "%s", buf);
|
|
|
|
if ((IsOper(source_p) || !ConfigServerHide.hide_servers) || target_p == source_p)
|
|
sendto_one(source_p, form_str(RPL_WHOISSERVER),
|
|
me.name, source_p->name, target_p->name, !target_p->servptr->hidden_server ? server_name : ServerInfo.network_name,
|
|
target_p->servptr->hidden_server ? ServerInfo.network_desc : a2client_p ? a2client_p->info:"*Not On This Net*");
|
|
else
|
|
sendto_one(source_p, form_str(RPL_WHOISSERVER),
|
|
me.name, source_p->name, target_p->name,
|
|
ServerInfo.network_name,
|
|
ServerInfo.network_desc);
|
|
|
|
if (target_p->user->away)
|
|
sendto_one(source_p, form_str(RPL_AWAY), me.name,
|
|
source_p->name, target_p->name, target_p->user->away);
|
|
|
|
if (IsOper(target_p))
|
|
{
|
|
sendto_one(source_p, form_str(RPL_WHOISOPERATOR),
|
|
me.name, source_p->name, target_p->name);
|
|
|
|
if (IsAdmin(target_p))
|
|
sendto_one(source_p, form_str(RPL_WHOISADMIN),
|
|
me.name, source_p->name, target_p->name);
|
|
}
|
|
|
|
if (IsServices(target_p))
|
|
sendto_one(source_p, form_str(RPL_WHOISSERVICES),
|
|
me.name, source_p->name, target_p->name);
|
|
if (IsOper(source_p) || (source_p == target_p))
|
|
{
|
|
send_umode(NULL, target_p, 0, ALL_UMODES, ubuf);
|
|
if (!*ubuf)
|
|
{
|
|
ubuf[0] = '+';
|
|
ubuf[1] = '\0';
|
|
}
|
|
sendto_one(source_p, form_str(RPL_WHOISMODES),
|
|
me.name, source_p->name, target_p->name, ubuf);
|
|
|
|
sendto_one(source_p, form_str(RPL_WHOISREALHOST),
|
|
me.name, source_p->name, target_p->name, target_p->host);
|
|
}
|
|
if (target_p->umodes & FLAGS_REGNICK) {
|
|
sendto_one(source_p, form_str(RPL_WHOISREGNICK),
|
|
me.name, source_p->name, target_p->name);
|
|
}
|
|
if (target_p->umodes & FLAGS_SSL) {
|
|
sendto_one(source_p, form_str(RPL_USINGSSL),
|
|
me.name, source_p->name, target_p->name);
|
|
}
|
|
if ( (glob == 1) || (MyConnect(target_p) && (IsOper(source_p) ||
|
|
!ConfigServerHide.hide_servers)) || (target_p == source_p) )
|
|
{
|
|
sendto_one(source_p, form_str(RPL_WHOISIDLE),
|
|
me.name, source_p->name, target_p->name,
|
|
CurrentTime - target_p->user->last,
|
|
target_p->firsttime);
|
|
}
|
|
|
|
hd.client_p = target_p;
|
|
hd.source_p = source_p;
|
|
|
|
/* although we should fill in parc and parv, we don't ..
|
|
* be careful of this when writing whois hooks
|
|
*/
|
|
if(MyClient(source_p))
|
|
hook_call_event("doing_whois", &hd);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
** ms_whois
|
|
** parv[0] = sender prefix
|
|
** parv[1] = nickname masklist
|
|
*/
|
|
/* Be warned, this is heavily commented, as theres loads of possibilities
|
|
* that can happen, and as people might not understand the code, I
|
|
* stuck heavy comments in it.. it looks ugly.. but at least you
|
|
* know what it does.. --fl_ */
|
|
static void
|
|
ms_whois(struct Client *client_p, struct Client *source_p,
|
|
int parc, char *parv[])
|
|
{
|
|
/* its a misconfigured server */
|
|
/* XXX Need BadPtr */
|
|
if (parc < 2 || BadPtr(parv[1]))
|
|
{
|
|
sendto_one(source_p, form_str(ERR_NONICKNAMEGIVEN),
|
|
me.name, parv[0]);
|
|
return;
|
|
}
|
|
|
|
if(!IsClient(source_p))
|
|
return;
|
|
|
|
/* its a client doing a remote whois:
|
|
* :parv[0] WHOIS parv[1] :parv[2]
|
|
*
|
|
* parv[0] == sender
|
|
* parv[1] == server to reply to the request
|
|
* parv[2] == the client we are whois'ing
|
|
*/
|
|
if(parc > 2)
|
|
{
|
|
struct Client *target_p;
|
|
|
|
/* check if parv[1] is a client.. (most common) */
|
|
if((target_p = find_client(parv[1])) == NULL)
|
|
{
|
|
/* ok, parv[1] isnt a client, is it a server? */
|
|
if((target_p = find_server(parv[1])) == NULL)
|
|
{
|
|
/* its not a server either.. theyve sent a bad whois */
|
|
sendto_one(source_p, form_str(ERR_NOSUCHSERVER), me.name,
|
|
parv[0], parv[1]);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* its been found as a client.. to save extra work later, change
|
|
* target_p to be the clients uplink.. you cant check if a client
|
|
* supports being a LL :P
|
|
*/
|
|
else
|
|
target_p = target_p->servptr;
|
|
|
|
/* if parv[1] isnt my client, or me, someone else is supposed
|
|
* to be handling the request.. so send it to them
|
|
*/
|
|
if(!MyClient(target_p) && !IsMe(target_p))
|
|
{
|
|
|
|
/* we're being asked to forward a whois request to a LL to answer,
|
|
* if the target isnt on that server.. answer it for them, as the
|
|
* LL might not know that the target exists..
|
|
*/
|
|
if(MyConnect(target_p) && ServerInfo.hub && IsCapable(target_p, CAP_LL))
|
|
{
|
|
struct Client *whois_p;
|
|
|
|
/* try to find the client */
|
|
if ((whois_p = find_client(parv[2])) != NULL)
|
|
{
|
|
/* check the server being asked to perform the whois, is that
|
|
* clients uplink */
|
|
if(whois_p->servptr != target_p)
|
|
{
|
|
/* they asked the LL for info on a client it didnt know about..
|
|
* as we're answering for them, make sure its non-detailed
|
|
*/
|
|
parv[1] = parv[2];
|
|
parc = 2;
|
|
|
|
do_whois(client_p, source_p, parc, parv);
|
|
return;
|
|
}
|
|
|
|
/* the server is that clients uplink, get them to do it */
|
|
else
|
|
{
|
|
client_burst_if_needed(target_p->from, source_p);
|
|
sendto_one(target_p->from, ":%s WHOIS %s :%s", parv[0], parv[1],
|
|
parv[2]);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* the client doesnt exist.. erk! */
|
|
else
|
|
{
|
|
/* set parv[1] = parv[2], then let do_whois handle the error */
|
|
parv[1] = parv[2];
|
|
do_whois(client_p, source_p, parc, parv);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* its not a lazylink.. forward it as it is */
|
|
else
|
|
{
|
|
/* client_burst_if_needed(target_p->from, source_p);
|
|
* the target isnt a LL.. why would I need to burst? */
|
|
sendto_one(target_p->from, ":%s WHOIS %s :%s", parv[0], parv[1],
|
|
parv[2]);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* ok, the target is either us, or a client on our server, so perform the whois
|
|
* but first, parv[1] == server to perform the whois on, parv[2] == person
|
|
* to whois, so make parv[1] = parv[2] so do_whois is ok -- fl_
|
|
*/
|
|
parv[1] = parv[2];
|
|
do_whois(client_p,source_p,parc,parv);
|
|
return;
|
|
}
|
|
|
|
/* parc == 2, so its a lazylink client asking us about a nick, so do it */
|
|
do_whois(client_p, source_p, parc, parv);
|
|
return;
|
|
}
|