321 lines
8.6 KiB
C
321 lines
8.6 KiB
C
/*
|
|
* IRC - Internet Relay Chat, ircd/s_user.c (formerly ircd/s_msg.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.
|
|
*/
|
|
/** @file
|
|
* @brief Support functions for /WHO-like commands.
|
|
* @version $Id: whocmds.c 1838 2007-10-30 01:53:33Z entrope $
|
|
*/
|
|
#include "config.h"
|
|
|
|
#include "whocmds.h"
|
|
#include "channel.h"
|
|
#include "client.h"
|
|
#include "hash.h"
|
|
#include "ircd.h"
|
|
#include "ircd_chattr.h"
|
|
#include "ircd_features.h"
|
|
#include "ircd_reply.h"
|
|
#include "ircd_snprintf.h"
|
|
#include "ircd_string.h"
|
|
#include "list.h"
|
|
#include "match.h"
|
|
#include "numeric.h"
|
|
#include "numnicks.h"
|
|
#include "querycmds.h"
|
|
#include "random.h"
|
|
#include "s_bsd.h"
|
|
#include "s_conf.h"
|
|
#include "s_misc.h"
|
|
#include "s_user.h"
|
|
#include "send.h"
|
|
#include "struct.h"
|
|
#include "sys.h"
|
|
#include "userload.h"
|
|
#include "version.h"
|
|
#include "whowas.h"
|
|
#include "msg.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
/** Send a WHO reply to a client who asked.
|
|
* @param[in] sptr Client who is searching for other users.
|
|
* @param[in] acptr Client who may be shown to \a sptr.
|
|
* @param[in] repchan Shared channel that provides visibility.
|
|
* @param[in] fields Bitmask of WHO_FIELD_* values, indicating what to show.
|
|
* @param[in] qrt Query type string (ignored unless \a fields & WHO_FIELD_QTY).
|
|
*/
|
|
void do_who(struct Client* sptr, struct Client* acptr, struct Channel* repchan,
|
|
int fields, char* qrt)
|
|
{
|
|
char *p1;
|
|
struct Membership *chan = 0;
|
|
|
|
static char buf1[512];
|
|
/* NOTE: with current fields list and sizes this _cannot_ overrun,
|
|
and also the message finally sent shouldn't ever be truncated */
|
|
|
|
p1 = buf1;
|
|
buf1[1] = '\0';
|
|
|
|
/* If we don't have a channel and we need one... try to find it,
|
|
unless the listing is for a channel service, we already know
|
|
that there are no common channels, thus use PubChannel and not
|
|
SeeChannel */
|
|
if (repchan)
|
|
{
|
|
chan = find_channel_member(acptr, repchan);
|
|
}
|
|
else if ((!fields || (fields & (WHO_FIELD_CHA | WHO_FIELD_FLA)))
|
|
&& !IsChannelService(acptr))
|
|
{
|
|
for (chan = cli_user(acptr)->channel; chan; chan = chan->next_channel)
|
|
if (PubChannel(chan->channel) &&
|
|
(acptr == sptr || !IsZombie(chan)))
|
|
break;
|
|
}
|
|
|
|
/* Place the fields one by one in the buffer and send it
|
|
note that fields == NULL means "default query" */
|
|
|
|
if (fields & WHO_FIELD_QTY) /* Query type */
|
|
{
|
|
*(p1++) = ' ';
|
|
if (BadPtr(qrt))
|
|
*(p1++) = '0';
|
|
else
|
|
while ((*qrt) && (*(p1++) = *(qrt++)));
|
|
}
|
|
|
|
if (!fields || (fields & WHO_FIELD_CHA))
|
|
{
|
|
char *p2;
|
|
*(p1++) = ' ';
|
|
if ((p2 = (chan ? chan->channel->chname : NULL)))
|
|
while ((*p2) && (*(p1++) = *(p2++)));
|
|
else
|
|
*(p1++) = '*';
|
|
}
|
|
|
|
if (!fields || (fields & WHO_FIELD_UID))
|
|
{
|
|
char *p2 = cli_user(acptr)->username;
|
|
*(p1++) = ' ';
|
|
while ((*p2) && (*(p1++) = *(p2++)));
|
|
}
|
|
|
|
if (fields & WHO_FIELD_NIP)
|
|
{
|
|
const char* p2 = IsHiddenHost(acptr) && !IsAnOper(sptr) ?
|
|
(!IsCloakIP(acptr) ? (IsAccount(acptr) ?
|
|
feature_str(FEAT_HIDDEN_IP) : ircd_ntoa(&cli_ip(acptr))) :
|
|
cli_user(acptr)->cloakip) : ircd_ntoa(&cli_ip(acptr));
|
|
*(p1++) = ' ';
|
|
while ((*p2) && (*(p1++) = *(p2++)));
|
|
}
|
|
|
|
if (!fields || (fields & WHO_FIELD_HOS))
|
|
{
|
|
char *p2 = cli_user(acptr)->host;
|
|
*(p1++) = ' ';
|
|
while ((*p2) && (*(p1++) = *(p2++)));
|
|
}
|
|
|
|
if (!fields || (fields & WHO_FIELD_SER))
|
|
{
|
|
const char *p2 = (feature_bool(FEAT_HIS_WHO_SERVERNAME) && !IsAnOper(sptr)) ?
|
|
feature_str(FEAT_HIS_SERVERNAME) :
|
|
cli_name(cli_user(acptr)->server);
|
|
*(p1++) = ' ';
|
|
while ((*p2) && (*(p1++) = *(p2++)));
|
|
}
|
|
|
|
if (!fields || (fields & WHO_FIELD_NIC))
|
|
{
|
|
char *p2 = cli_name(acptr);
|
|
*(p1++) = ' ';
|
|
while ((*p2) && (*(p1++) = *(p2++)));
|
|
}
|
|
|
|
if (!fields || (fields & WHO_FIELD_FLA))
|
|
{
|
|
*(p1++) = ' ';
|
|
if (cli_user(acptr)->away)
|
|
*(p1++) = 'G';
|
|
else
|
|
*(p1++) = 'H';
|
|
if SeeOper(sptr,acptr)
|
|
*(p1++) = '*';
|
|
if (!chan) {
|
|
/* No flags possible for the channel, so skip them all. */
|
|
}
|
|
else if ((IsNamesX(sptr) || CapActive(sptr, CAP_NAMESX)) || fields) {
|
|
/* If you specified flags then we assume you know how to parse
|
|
* multiple channel status flags, as this is currently the only
|
|
* way to know if someone has @'s *and* is +'d.
|
|
*/
|
|
if (IsChanOp(chan))
|
|
*(p1++) = '@';
|
|
if (IsHalfOp(chan))
|
|
*(p1++) = '%';
|
|
if (HasVoice(chan))
|
|
*(p1++) = '+';
|
|
if (IsZombie(chan))
|
|
*(p1++) = '!';
|
|
if (IsDelayedJoin(chan))
|
|
*(p1++) = '<';
|
|
}
|
|
else {
|
|
if (IsChanOp(chan))
|
|
*(p1++) = '@';
|
|
else if (IsHalfOp(chan))
|
|
*(p1++) = '%';
|
|
else if (HasVoice(chan))
|
|
*(p1++) = '+';
|
|
else if (IsZombie(chan))
|
|
*(p1++) = '!';
|
|
else if (IsDelayedJoin(chan))
|
|
*(p1++) = '<';
|
|
}
|
|
if (IsDeaf(acptr))
|
|
*(p1++) = 'd';
|
|
if (IsAnOper(sptr))
|
|
{
|
|
if (IsInvisible(acptr))
|
|
*(p1++) = 'i';
|
|
if (SendWallops(acptr))
|
|
*(p1++) = 'w';
|
|
if (SendDebug(acptr))
|
|
*(p1++) = 'g';
|
|
}
|
|
if (IsHiddenHost(acptr))
|
|
*(p1++) = 'x';
|
|
if (IsSSL(acptr))
|
|
*(p1++) = 'z';
|
|
if (IsBot(acptr))
|
|
*(p1++) = 'B';
|
|
}
|
|
|
|
if (!fields || (fields & WHO_FIELD_DIS))
|
|
{
|
|
*p1++ = ' ';
|
|
if (!fields)
|
|
*p1++ = ':'; /* Place colon here for default reply */
|
|
if (feature_bool(FEAT_HIS_WHO_HOPCOUNT) && !IsAnOper(sptr))
|
|
*p1++ = (sptr == acptr) ? '0' : '3';
|
|
else
|
|
/* three digit hopcount maximum */
|
|
p1 += ircd_snprintf(0, p1, 3, "%d", cli_hopcount(acptr));
|
|
}
|
|
|
|
if (fields & WHO_FIELD_IDL)
|
|
{
|
|
*p1++ = ' ';
|
|
if (MyUser(acptr) &&
|
|
(IsAnOper(sptr) || !feature_bool(FEAT_HIS_WHO_SERVERNAME) ||
|
|
acptr == sptr))
|
|
p1 += ircd_snprintf(0, p1, 11, "%d",
|
|
CurrentTime - cli_user(acptr)->last);
|
|
else
|
|
*p1++ = '0';
|
|
}
|
|
|
|
if (fields & WHO_FIELD_ACC)
|
|
{
|
|
char *p2 = cli_user(acptr)->account;
|
|
*(p1++) = ' ';
|
|
if (*p2)
|
|
while ((*p2) && (*(p1++) = *(p2++)));
|
|
else
|
|
*(p1++) = '0';
|
|
}
|
|
|
|
if (fields & WHO_FIELD_OPL)
|
|
{
|
|
if (!chan || !IsChanOp(chan))
|
|
{
|
|
/* Security fix: Replace strcpy cu memcpy pentru siguranță */
|
|
memcpy(p1, " n/a", 5); /* Include null terminator */
|
|
p1 += 4;
|
|
}
|
|
else
|
|
{
|
|
int vis_level = MAXOPLEVEL;
|
|
if ((IsGlobalChannel(chan->channel->chname) ? IsOper(sptr) : IsAnOper(sptr))
|
|
|| is_chan_op(sptr, chan->channel))
|
|
vis_level = OpLevel(chan);
|
|
p1 += ircd_snprintf(0, p1, 5, " %d", vis_level);
|
|
}
|
|
}
|
|
|
|
if (fields & WHO_FIELD_MRK)
|
|
{
|
|
*(p1++) = ' ';
|
|
|
|
if (IsMarked(acptr)) {
|
|
char markbuf[128];
|
|
char *p2 = (char *)&markbuf;
|
|
int len = 0;
|
|
struct SLink* dp = NULL;
|
|
|
|
for (dp = cli_marks(acptr); dp; dp = dp->next) {
|
|
len += strlen(dp->value.cp) + 1;
|
|
if (len > 128)
|
|
break;
|
|
strncpy(p2, dp->value.cp, 128-len);
|
|
p2 += strlen(dp->value.cp);
|
|
*p2++ = ',';
|
|
}
|
|
if (p2 > (char *)&markbuf)
|
|
*--p2 = '\0'; /* Replace trailing comma with null */
|
|
else
|
|
*p2 = '\0'; /* Empty string if no marks */
|
|
|
|
if (len > 128)
|
|
strncpy((char *)&markbuf, "*ManyMarks*", 12);
|
|
|
|
p2 = (char *)&markbuf;
|
|
while ((*p2) && (*(p1++) = *(p2++)));
|
|
} else
|
|
*(p1++) = '0';
|
|
}
|
|
|
|
if (!fields || (fields & WHO_FIELD_REN))
|
|
{
|
|
char *p2 = cli_info(acptr);
|
|
*p1++ = ' ';
|
|
if (fields)
|
|
*p1++ = ':'; /* Place colon here for special reply */
|
|
while ((*p2) && (*(p1++) = *(p2++)));
|
|
}
|
|
|
|
/* The first char will always be an useless blank and we
|
|
need to terminate buf1 */
|
|
*p1 = '\0';
|
|
p1 = buf1;
|
|
send_reply(sptr, fields ? RPL_WHOSPCRPL : RPL_WHOREPLY, ++p1);
|
|
}
|