2 * IRC - Internet Relay Chat, ircd/s_user.c (formerly ircd/s_msg.c)
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Computing Center
6 * See file AUTHORS in IRC package for additional names of
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 1, or (at your option)
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 * @brief Support functions for /WHO-like commands.
34 #include "ircd_chattr.h"
35 #include "ircd_features.h"
36 #include "ircd_reply.h"
37 #include "ircd_snprintf.h"
38 #include "ircd_string.h"
43 #include "querycmds.h"
64 /** Send a WHO reply to a client who asked.
65 * @param[in] sptr Client who is searching for other users.
66 * @param[in] acptr Client who may be shown to \a sptr.
67 * @param[in] repchan Shared channel that provides visibility.
68 * @param[in] fields Bitmask of WHO_FIELD_* values, indicating what to show.
69 * @param[in] qrt Query type string (ignored unless \a fields & WHO_FIELD_QTY).
71 void do_who(struct Client* sptr, struct Client* acptr, struct Channel* repchan,
72 int fields, char* qrt)
75 struct Membership *chan = 0;
77 static char buf1[512];
78 /* NOTE: with current fields list and sizes this _cannot_ overrun,
79 and also the message finally sent shouldn't ever be truncated */
84 /* If we don't have a channel and we need one... try to find it,
85 unless the listing is for a channel service, we already know
86 that there are no common channels, thus use PubChannel and not
89 chan = find_channel_member(acptr, repchan);
90 else if ((!fields || (fields & (WHO_FIELD_CHA | WHO_FIELD_FLA)))
91 && !IsChannelService(acptr))
93 for (chan = cli_user(acptr)->channel; chan; chan = chan->next_channel)
94 if (PubChannel(chan->channel) &&
95 (acptr == sptr || !IsZombie(chan)))
99 /* Place the fields one by one in the buffer and send it
100 note that fields == NULL means "default query" */
102 if (fields & WHO_FIELD_QTY) /* Query type */
108 while ((*qrt) && (*(p1++) = *(qrt++)));
111 if (!fields || (fields & WHO_FIELD_CHA))
115 if ((p2 = (chan ? chan->channel->chname : NULL)))
116 while ((*p2) && (*(p1++) = *(p2++)));
121 if (!fields || (fields & WHO_FIELD_UID))
123 char *p2 = cli_user(acptr)->username;
125 while ((*p2) && (*(p1++) = *(p2++)));
128 if (fields & WHO_FIELD_NIP)
130 const char* p2 = HasHiddenHost(acptr) && !IsAnOper(sptr) ?
131 feature_str(FEAT_HIDDEN_IP) :
132 ircd_ntoa(&cli_ip(acptr));
134 while ((*p2) && (*(p1++) = *(p2++)));
137 if (!fields || (fields & WHO_FIELD_HOS))
139 char *p2 = cli_user(acptr)->host;
141 while ((*p2) && (*(p1++) = *(p2++)));
144 if (!fields || (fields & WHO_FIELD_SER))
146 const char *p2 = (feature_bool(FEAT_HIS_WHO_SERVERNAME) && !IsAnOper(sptr)) ?
147 feature_str(FEAT_HIS_SERVERNAME) :
148 cli_name(cli_user(acptr)->server);
150 while ((*p2) && (*(p1++) = *(p2++)));
153 if (!fields || (fields & WHO_FIELD_NIC))
155 char *p2 = cli_name(acptr);
157 while ((*p2) && (*(p1++) = *(p2++)));
160 if (!fields || (fields & WHO_FIELD_FLA))
163 if (cli_user(acptr)->away)
167 if SeeOper(sptr,acptr)
170 /* No flags possible for the channel, so skip them all. */
173 /* If you specified flags then we assume you know how to parse
174 * multiple channel status flags, as this is currently the only
175 * way to know if someone has @'s *and* is +'d.
183 if (IsDelayedJoin(chan))
189 else if (HasVoice(chan))
191 else if (IsZombie(chan))
193 else if (IsDelayedJoin(chan))
200 if (IsInvisible(acptr))
202 if (SendWallops(acptr))
204 if (SendDebug(acptr))
207 if (HasHiddenHost(acptr))
211 if (!fields || (fields & WHO_FIELD_DIS))
215 *p1++ = ':'; /* Place colon here for default reply */
216 if (feature_bool(FEAT_HIS_WHO_HOPCOUNT) && !IsAnOper(sptr))
217 *p1++ = (sptr == acptr) ? '0' : '3';
219 /* three digit hopcount maximum */
220 p1 += ircd_snprintf(0, p1, 3, "%d", cli_hopcount(acptr));
223 if (fields & WHO_FIELD_IDL)
227 (IsAnOper(sptr) || !feature_bool(FEAT_HIS_WHO_SERVERNAME) ||
229 p1 += ircd_snprintf(0, p1, 11, "%d",
230 CurrentTime - cli_user(acptr)->last);
235 if (fields & WHO_FIELD_ACC)
237 char *p2 = cli_user(acptr)->account;
240 while ((*p2) && (*(p1++) = *(p2++)));
245 if (!fields || (fields & WHO_FIELD_REN))
247 char *p2 = cli_info(acptr);
250 *p1++ = ':'; /* Place colon here for special reply */
251 while ((*p2) && (*(p1++) = *(p2++)));
254 /* The first char will always be an useless blank and we
255 need to terminate buf1 */
258 send_reply(sptr, fields ? RPL_WHOSPCRPL : RPL_WHOREPLY, ++p1);
261 /** Count number of users who match \a mask.
262 * @param[in] mask user\@host or user\@ip mask to check.
263 * @return Count of matching users.
266 count_users(char *mask)
268 struct Client *acptr;
270 char namebuf[USERLEN + HOSTLEN + 2];
271 char ipbuf[USERLEN + 16 + 2];
273 for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
277 ircd_snprintf(0, namebuf, sizeof(namebuf), "%s@%s",
278 cli_user(acptr)->username, cli_user(acptr)->host);
279 ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%s@%s", cli_user(acptr)->username,
280 ircd_ntoa(&cli_ip(acptr)));
282 if (!match(mask, namebuf) || !match(mask, ipbuf))