* 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.
- *
- * $Id$
+ */
+/** @file
+ * @brief Support functions for /WHO-like commands.
+ * @version $Id$
*/
#include "config.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 "s_misc.h"
#include "s_user.h"
#include "send.h"
-#include "sprintf_irc.h"
#include "struct.h"
-#include "support.h"
#include "sys.h"
#include "userload.h"
#include "version.h"
#include "whowas.h"
#include "msg.h"
-#include <arpa/inet.h> /* inet_ntoa */
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
-/*
- * The function that actually prints out the WHO reply for a client found
+/** 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 Channel *chptr = repchan;
+ struct Membership *chan = 0;
static char buf1[512];
/* NOTE: with current fields list and sizes this _cannot_ overrun,
unless the listing is for a channel service, we already know
that there are no common channels, thus use PubChannel and not
SeeChannel */
- if (!chptr && (!fields || (fields & (WHO_FIELD_CHA | WHO_FIELD_FLA))) &&
- !IsChannelService(acptr))
+ if (repchan)
{
- struct Membership* chan;
- for (chan = cli_user(acptr)->channel; chan && !chptr; chan = chan->next_channel)
+ 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)))
- chptr = chan->channel;
+ break;
}
/* Place the fields one by one in the buffer and send it
{
char *p2;
*(p1++) = ' ';
- if ((p2 = (chptr ? chptr->chname : NULL)))
+ if ((p2 = (chan ? chan->channel->chname : NULL)))
while ((*p2) && (*(p1++) = *(p2++)));
else
*(p1++) = '*';
if (fields & WHO_FIELD_NIP)
{
- const char* p2 = ircd_ntoa((const char*) &(cli_ip(acptr)));
+ const char* p2 = HasHiddenHost(acptr) && !IsAnOper(sptr) ?
+ feature_str(FEAT_HIDDEN_IP) :
+ ircd_ntoa(&cli_ip(acptr));
*(p1++) = ' ';
while ((*p2) && (*(p1++) = *(p2++)));
}
if (!fields || (fields & WHO_FIELD_SER))
{
- char *p2 = cli_name(cli_user(acptr)->server);
+ 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++)));
}
*(p1++) = 'G';
else
*(p1++) = 'H';
- if (IsAnOper(acptr) &&
- (HasPriv(acptr, PRIV_DISPLAY) || HasPriv(sptr, PRIV_SEE_OPERS)))
+ if SeeOper(sptr,acptr)
*(p1++) = '*';
- if (fields) {
+ if (!chan) {
+ /* No flags possible for the channel, so skip them all. */
+ }
+ else if (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 (chptr && is_chan_op(acptr, chptr))
+ if (IsChanOp(chan))
*(p1++) = '@';
- if (chptr && has_voice(acptr, chptr))
+ if (HasVoice(chan))
*(p1++) = '+';
- if (chptr && is_zombie(acptr, chptr))
+ if (IsZombie(chan))
*(p1++) = '!';
+ if (IsDelayedJoin(chan))
+ *(p1++) = '<';
}
else {
- if (chptr && is_chan_op(acptr, chptr))
+ if (IsChanOp(chan))
*(p1++) = '@';
- else if (chptr && has_voice(acptr, chptr))
+ else if (HasVoice(chan))
*(p1++) = '+';
- else if (chptr && is_zombie(acptr, chptr))
+ else if (IsZombie(chan))
*(p1++) = '!';
+ else if (IsDelayedJoin(chan))
+ *(p1++) = '<';
}
if (IsDeaf(acptr))
*(p1++) = 'd';
if (SendDebug(acptr))
*(p1++) = 'g';
}
+ if (HasHiddenHost(acptr))
+ *(p1++) = 'x';
}
if (!fields || (fields & WHO_FIELD_DIS))
*p1++ = ' ';
if (!fields)
*p1++ = ':'; /* Place colon here for default reply */
- p1 = sprintf_irc(p1, "%d", cli_hopcount(acptr));
+ 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)) {
- p1 = sprintf_irc(p1, "%d", CurrentTime - cli_user(acptr)->last);
- }
- else {
- *p1++ = '0';
- }
+ 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))
+ {
+ strcpy(p1, " n/a");
+ 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 || (fields & WHO_FIELD_REN))
p1 = buf1;
send_reply(sptr, fields ? RPL_WHOSPCRPL : RPL_WHOREPLY, ++p1);
}
-