ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/modules/m_who.c
Revision: 9857
Committed: Fri Jan 1 04:43:22 2021 UTC (4 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 11626 byte(s)
Log Message:
- Bump copyright years

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2021 ircd-hybrid development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 /*! \file m_who.c
23 * \brief Includes required functions for processing the WHO command.
24 * \version $Id$
25 */
26
27 #include "stdinc.h"
28 #include "list.h"
29 #include "client.h"
30 #include "channel.h"
31 #include "channel_mode.h"
32 #include "hash.h"
33 #include "ircd.h"
34 #include "numeric.h"
35 #include "server.h"
36 #include "send.h"
37 #include "irc_string.h"
38 #include "conf.h"
39 #include "parse.h"
40 #include "modules.h"
41 #include "hostmask.h"
42
43
44 enum { WHO_MAX_REPLIES = 500 };
45
46
47 /* do_who()
48 *
49 * inputs - pointer to client requesting who
50 * - pointer to client to do who on
51 * - The reported name
52 * - channel flags
53 * output - NONE
54 * side effects - do a who on given person
55 */
56 static void
57 do_who(struct Client *source_p, const struct Client *target_p,
58 const char *name, const char *op_flags)
59 {
60 char status[8]; /* sizeof("GBr*@%+") + 1 */
61
62 if (HasUMode(source_p, UMODE_OPER))
63 snprintf(status, sizeof(status), "%c%s%s%s%s", target_p->away[0] ? 'G' : 'H',
64 HasUMode(target_p, UMODE_BOT) ? "B" : "",
65 HasUMode(target_p, UMODE_REGISTERED) ? "r" : "",
66 HasUMode(target_p, UMODE_OPER) ? "*" : "", op_flags);
67 else
68 snprintf(status, sizeof(status), "%c%s%s%s%s", target_p->away[0] ? 'G' : 'H',
69 HasUMode(target_p, UMODE_BOT) ? "B" : "",
70 HasUMode(target_p, UMODE_REGISTERED) ? "r" : "",
71 HasUMode(target_p, UMODE_OPER) &&
72 !HasUMode(target_p, UMODE_HIDDEN) ? "*" : "", op_flags);
73
74 if (ConfigServerHide.hide_servers || IsHidden(target_p->servptr))
75 sendto_one_numeric(source_p, &me, RPL_WHOREPLY,
76 (name) ? (name) : "*",
77 target_p->username, target_p->host,
78 HasUMode(source_p, UMODE_OPER) ? target_p->servptr->name : "*",
79 target_p->name, status,
80 HasUMode(source_p, UMODE_OPER) ? target_p->hopcount : 0, target_p->info);
81 else
82 sendto_one_numeric(source_p, &me, RPL_WHOREPLY,
83 (name) ? (name) : "*", target_p->username,
84 target_p->host, target_p->servptr->name, target_p->name,
85 status, target_p->hopcount, target_p->info);
86 }
87
88 /*!
89 * \param source_p Pointer to client requesting who
90 * \param target_p Pointer to client to do who on
91 * \param mask Mask to match
92 * \return true if mask matches, false otherwise
93 */
94 static bool
95 who_matches(struct Client *source_p, struct Client *target_p, const char *mask)
96 {
97 if (mask == NULL)
98 return true;
99
100 if (match(mask, target_p->name) == 0)
101 return true;
102
103 if (match(mask, target_p->username) == 0)
104 return true;
105
106 if (match(mask, target_p->host) == 0)
107 return true;
108
109 if (match(mask, target_p->info) == 0)
110 return true;
111
112 if (HasUMode(source_p, UMODE_OPER))
113 {
114 int bits = 0;
115 struct irc_ssaddr addr;
116
117 switch (parse_netmask(mask, &addr, &bits))
118 {
119 case HM_IPV6:
120 case HM_IPV4:
121 if (address_compare(&target_p->ip, &addr, false, false, bits) == true)
122 return true;
123 }
124
125 if (match(mask, target_p->sockhost) == 0)
126 return true;
127 if (match(mask, target_p->realhost) == 0)
128 return true;
129 }
130
131 if (HasUMode(source_p, UMODE_OPER) ||
132 (ConfigServerHide.hide_servers == 0 && !IsHidden(target_p->servptr)))
133 if (match(mask, target_p->servptr->name) == 0)
134 return true;
135
136 return false;
137 }
138
139 /* who_common_channel
140 * inputs - pointer to client requesting who
141 * - pointer to channel member chain.
142 * - char * mask to match
143 * - int if oper on a server or not
144 * - pointer to int maxmatches
145 * output - NONE
146 * side effects - lists matching clients on specified channel,
147 * marks matched clients.
148 *
149 */
150 static void
151 who_common_channel(struct Client *source_p, struct Channel *channel, const char *mask,
152 bool server_oper, unsigned int *maxmatches)
153 {
154 dlink_node *node;
155
156 DLINK_FOREACH(node, channel->members.head)
157 {
158 struct Client *target_p = ((struct ChannelMember *)node->data)->client;
159
160 if (!HasUMode(target_p, UMODE_INVISIBLE) || HasFlag(target_p, FLAGS_MARK))
161 continue;
162
163 if (server_oper == true)
164 if (!HasUMode(target_p, UMODE_OPER) ||
165 (HasUMode(target_p, UMODE_HIDDEN) && !HasUMode(source_p, UMODE_OPER)))
166 continue;
167
168 AddFlag(target_p, FLAGS_MARK);
169
170 if (who_matches(source_p, target_p, mask) == true)
171 {
172 do_who(source_p, target_p, NULL, "");
173
174 if (*maxmatches)
175 {
176 if (--(*maxmatches) == 0)
177 {
178 sendto_one_numeric(source_p, &me, ERR_WHOLIMEXCEED, WHO_MAX_REPLIES, "WHO");
179 return;
180 }
181 }
182 }
183 }
184 }
185
186 /* who_global()
187 *
188 * inputs - pointer to client requesting who
189 * - char * mask to match
190 * - int if oper on a server or not
191 * output - NONE
192 * side effects - do a global scan of all clients looking for match
193 * this is slightly expensive on EFnet ...
194 */
195 static void
196 who_global(struct Client *source_p, const char *mask, bool server_oper)
197 {
198 dlink_node *node;
199 unsigned int maxmatches = WHO_MAX_REPLIES;
200 static uintmax_t last_used = 0;
201
202 if (!HasUMode(source_p, UMODE_OPER))
203 {
204 if ((last_used + ConfigGeneral.pace_wait) > event_base->time.sec_monotonic)
205 {
206 sendto_one_numeric(source_p, &me, RPL_LOAD2HI, "WHO");
207 return;
208 }
209
210 last_used = event_base->time.sec_monotonic;
211 }
212
213 /* First, list all matching invisible clients on common channels */
214 DLINK_FOREACH(node, source_p->channel.head)
215 {
216 struct Channel *channel = ((struct ChannelMember *)node->data)->channel;
217 who_common_channel(source_p, channel, mask, server_oper, &maxmatches);
218 }
219
220 /* Second, list all matching visible clients */
221 DLINK_FOREACH(node, global_client_list.head)
222 {
223 struct Client *target_p = node->data;
224
225 assert(IsClient(target_p));
226
227 if (HasUMode(target_p, UMODE_INVISIBLE))
228 {
229 DelFlag(target_p, FLAGS_MARK);
230 continue;
231 }
232
233 if (server_oper == true)
234 if (!HasUMode(target_p, UMODE_OPER) ||
235 (HasUMode(target_p, UMODE_HIDDEN) && !HasUMode(source_p, UMODE_OPER)))
236 continue;
237
238 if (who_matches(source_p, target_p, mask) == true)
239 {
240 do_who(source_p, target_p, NULL, "");
241
242 if (maxmatches)
243 {
244 if (--maxmatches == 0)
245 {
246 sendto_one_numeric(source_p, &me, ERR_WHOLIMEXCEED, WHO_MAX_REPLIES, "WHO");
247 return;
248 }
249 }
250 }
251 }
252 }
253
254 /* do_who_on_channel()
255 *
256 * inputs - pointer to client requesting who
257 * - pointer to channel to do who on
258 * - The "real name" of this channel
259 * - int if source_p is a server oper or not
260 * - int if client is member or not
261 * - int server_op flag
262 * output - NONE
263 * side effects - do a who on given channel
264 */
265 static void
266 do_who_on_channel(struct Client *source_p, struct Channel *channel,
267 bool is_member, bool server_oper)
268 {
269 dlink_node *node;
270
271 DLINK_FOREACH(node, channel->members.head)
272 {
273 struct ChannelMember *member = node->data;
274 struct Client *target_p = member->client;
275
276 if (is_member == true || !HasUMode(target_p, UMODE_INVISIBLE))
277 {
278 if (server_oper == true)
279 if (!HasUMode(target_p, UMODE_OPER) ||
280 (HasUMode(target_p, UMODE_HIDDEN) && !HasUMode(source_p, UMODE_OPER)))
281 continue;
282 do_who(source_p, target_p, channel->name, member_get_prefix(member, !!HasCap(source_p, CAP_MULTI_PREFIX)));
283 }
284 }
285 }
286
287 /*! \brief WHO command handler
288 *
289 * \param source_p Pointer to allocated Client struct from which the message
290 * originally comes from. This can be a local or remote client.
291 * \param parc Integer holding the number of supplied arguments.
292 * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL
293 * pointers.
294 * \note Valid arguments for this command are:
295 * - parv[0] = command
296 * - parv[1] = nickname/channelname
297 * - parv[2] = additional selection flag, only 'o' for now
298 */
299 static void
300 m_who(struct Client *source_p, int parc, char *parv[])
301 {
302 dlink_node *node;
303 struct Client *target_p = NULL;
304 struct Channel *channel = NULL;
305 char *const mask = parv[1];
306 const bool server_oper = parv[2] && *parv[2] == 'o'; /* Show OPERS only */
307
308 /* See if mask is there, collapse it or return if not there */
309 if (EmptyString(mask))
310 {
311 who_global(source_p, NULL, server_oper);
312 sendto_one_numeric(source_p, &me, RPL_ENDOFWHO, "*");
313 return;
314 }
315
316 /* Mask isn't NULL at this point. repeat after me... -db */
317 collapse(mask);
318
319 /* '/who #some_channel' */
320 if (IsChanPrefix(*mask))
321 {
322 /* List all users on a given channel */
323 if ((channel = hash_find_channel(mask)))
324 {
325 if (HasUMode(source_p, UMODE_ADMIN) || member_find_link(source_p, channel))
326 do_who_on_channel(source_p, channel, true, server_oper);
327 else if (!SecretChannel(channel))
328 do_who_on_channel(source_p, channel, false, server_oper);
329 }
330
331 sendto_one_numeric(source_p, &me, RPL_ENDOFWHO, mask);
332 return;
333 }
334
335 /* '/who nick' */
336 if ((target_p = find_person(source_p, mask)) &&
337 (server_oper == false || HasUMode(target_p, UMODE_OPER)))
338 {
339 DLINK_FOREACH(node, target_p->channel.head)
340 {
341 channel = ((struct ChannelMember *)node->data)->channel;
342 if (PubChannel(channel) || member_find_link(source_p, channel))
343 break;
344 }
345
346 if (node)
347 do_who(source_p, target_p, channel->name,
348 member_get_prefix(node->data, !!HasCap(source_p, CAP_MULTI_PREFIX)));
349 else
350 do_who(source_p, target_p, NULL, "");
351
352 sendto_one_numeric(source_p, &me, RPL_ENDOFWHO, mask);
353 return;
354 }
355
356 /* '/who *' */
357 if (strcmp(mask, "*") == 0)
358 {
359 if ((node = source_p->channel.head))
360 {
361 channel = ((struct ChannelMember *)node->data)->channel;
362 do_who_on_channel(source_p, channel, true, server_oper);
363 }
364
365 sendto_one_numeric(source_p, &me, RPL_ENDOFWHO, "*");
366 return;
367 }
368
369 /* '/who 0' */
370 if (strcmp(mask, "0") == 0)
371 who_global(source_p, NULL, server_oper);
372 else
373 who_global(source_p, mask, server_oper);
374
375 /* Wasn't a nick, wasn't a channel, wasn't a '*' so ... */
376 sendto_one_numeric(source_p, &me, RPL_ENDOFWHO, mask);
377 }
378
379 static struct Message who_msgtab =
380 {
381 .cmd = "WHO",
382 .handlers[UNREGISTERED_HANDLER] = { .handler = m_unregistered },
383 .handlers[CLIENT_HANDLER] = { .handler = m_who },
384 .handlers[SERVER_HANDLER] = { .handler = m_ignore },
385 .handlers[ENCAP_HANDLER] = { .handler = m_ignore },
386 .handlers[OPER_HANDLER] = { .handler = m_who }
387 };
388
389 static void
390 module_init(void)
391 {
392 mod_add_cmd(&who_msgtab);
393 }
394
395 static void
396 module_exit(void)
397 {
398 mod_del_cmd(&who_msgtab);
399 }
400
401 struct module module_entry =
402 {
403 .version = "$Revision$",
404 .modinit = module_init,
405 .modexit = module_exit,
406 };

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision