ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/branches/8.2.x/modules/m_who.c
Revision: 9607
Committed: Sun Sep 6 06:33:33 2020 UTC (5 years, 11 months ago) by michael
Content type: text/x-csrc
File size: 11511 byte(s)
Log Message:
- Stylistic changes

File Contents

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

Properties

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