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: 10198
Committed: Fri Jul 15 14:49:03 2022 UTC (3 years, 1 month ago) by michael
Content type: text/x-csrc
File size: 18141 byte(s)
Log Message:
- m_who.c:who_send(): fix to previous changes regarding the TLS flag

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2022 ircd-hybrid development team
5 * Copyright (c) 1997-1999 Andrea "Nemesi" Cocito
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20 * USA
21 */
22
23 /*! \file m_who.c
24 * \brief Includes required functions for processing the WHO command.
25 * \version $Id$
26 */
27
28 #include "stdinc.h"
29 #include "list.h"
30 #include "client.h"
31 #include "channel.h"
32 #include "channel_mode.h"
33 #include "hash.h"
34 #include "ircd.h"
35 #include "isupport.h"
36 #include "numeric.h"
37 #include "send.h"
38 #include "irc_string.h"
39 #include "conf.h"
40 #include "parse.h"
41 #include "modules.h"
42 #include "hostmask.h"
43
44
45 /** Maximum number of lines to send in response to a /WHO. */
46 enum { WHO_MAX_REPLIES = 500 };
47
48 enum
49 {
50 WHOSELECT_OPER = 1 << 0, /**< Flag for /WHO: Show IRC operators. */
51 WHOSELECT_EXTRA = 1 << 1, /**< Flag for /WHO: Pull rank to see users. */
52 WHOSELECT_DELAY = 1 << 2, /**< Flag for /WHO: Show join-delayed users. */
53 };
54
55 enum
56 {
57 WHO_FIELD_QTO = 1 << 0, /**< Display query token. */
58 WHO_FIELD_CHA = 1 << 1, /**< Show common channel name. */
59 WHO_FIELD_UID = 1 << 2, /**< Show username. */
60 WHO_FIELD_NIP = 1 << 3, /**< Show IP address. */
61 WHO_FIELD_HOS = 1 << 4, /**< Show hostname. */
62 WHO_FIELD_SER = 1 << 5, /**< Show server. */
63 WHO_FIELD_NIC = 1 << 6, /**< Show nickname. */
64 WHO_FIELD_FLA = 1 << 7, /**< Show flags (away, oper, chanop, etc). */
65 WHO_FIELD_DIS = 1 << 8, /**< Show hop count (distance). */
66 WHO_FIELD_REN = 1 << 9, /**< Show realname (info). */
67 WHO_FIELD_IDL = 1 << 10, /**< Show idle time. */
68 WHO_FIELD_ACC = 1 << 11, /**< Show account name. */
69 WHO_FIELD_OPL = 1 << 12, /**< Show oplevel. */
70 /** Default fields for /WHO */
71 WHO_FIELD_DEF = WHO_FIELD_NIC | WHO_FIELD_UID | WHO_FIELD_HOS | WHO_FIELD_SER,
72 };
73
74 struct WhoQuery
75 {
76 unsigned int bitsel; /**< User mode matching flags */
77 unsigned int matchsel; /**< Field matching flags */
78 unsigned int fields; /**< Fields to be shown in the output */
79 unsigned int maxmatches; /**< Maximum number of replies to be sent */
80 const char *token; /**< User-defined query token */
81 };
82
83
84 /*! \brief Send a WHO reply to a client who asked.
85 * \param source_p Pointer to client requesting who.
86 * \param target_p Client who is shown to \a source_p.
87 * \param member ChannelMember pointer of a shared channel that provides visibility.
88 * \param who Pointer to struct WhoQuery item that defines the options for this query.
89 */
90 static void
91 who_send(struct Client *source_p, const struct Client *target_p,
92 const struct ChannelMember *member, const struct WhoQuery *who)
93 {
94 char buf[IRCD_BUFSIZE];
95 char *p = buf;
96
97 /*
98 * NOTE: with current fields list and sizes this _cannot_ overrun,
99 * and also the message finally sent shouldn't ever be truncated.
100 */
101 buf[1] = '\0';
102
103 /* If we don't have a channel and we need one... try to find it. */
104 if (member == NULL)
105 {
106 if (who->fields == 0 || (who->fields & (WHO_FIELD_CHA | WHO_FIELD_FLA)))
107 {
108 dlink_node *node;
109 DLINK_FOREACH(node, target_p->channel.head)
110 {
111 member = node->data;
112
113 if (PubChannel(member->channel))
114 break;
115 member = NULL;
116 }
117 }
118 }
119
120 /*
121 * Place the fields in the buffer and send it. Note that who->fields == 0
122 * means "default query".
123 */
124
125 if ((who->fields & WHO_FIELD_QTO)) /* Query token */
126 {
127 if (EmptyString(who->token))
128 p += snprintf(p, sizeof(buf) - (p - buf), " %s", "0");
129 else
130 p += snprintf(p, sizeof(buf) - (p - buf), " %s", who->token);
131 }
132
133 if (who->fields == 0 || (who->fields & WHO_FIELD_CHA))
134 p += snprintf(p, sizeof(buf) - (p - buf), " %s", member ? member->channel->name : "*");
135
136 if (who->fields == 0 || (who->fields & WHO_FIELD_UID))
137 p += snprintf(p, sizeof(buf) - (p - buf), " %s", target_p->username);
138
139 if ((who->fields & WHO_FIELD_NIP))
140 {
141 if (HasUMode(source_p, UMODE_OPER) || source_p == target_p)
142 p += snprintf(p, sizeof(buf) - (p - buf), " %s", target_p->sockhost);
143 else
144 p += snprintf(p, sizeof(buf) - (p - buf), " %s", "255.255.255.255");
145 }
146
147 if (who->fields == 0 || (who->fields & WHO_FIELD_HOS))
148 p += snprintf(p, sizeof(buf) - (p - buf), " %s", target_p->host);
149
150 if (who->fields == 0 || (who->fields & WHO_FIELD_SER))
151 {
152 if (!HasUMode(source_p, UMODE_OPER) &&
153 (ConfigServerHide.hide_servers || IsHidden(target_p->servptr)))
154 p += snprintf(p, sizeof(buf) - (p - buf), " %s", ConfigServerHide.hidden_name);
155 else
156 p += snprintf(p, sizeof(buf) - (p - buf), " %s", target_p->servptr->name);
157 }
158
159 if (who->fields == 0 || (who->fields & WHO_FIELD_NIC))
160 p += snprintf(p, sizeof(buf) - (p - buf), " %s", target_p->name);
161
162 if (who->fields == 0 || (who->fields & WHO_FIELD_FLA))
163 {
164 char status[16];
165
166 if (HasUMode(source_p, UMODE_OPER))
167 snprintf(status, sizeof(status), "%c%s%s%s%s%s", target_p->away[0] ? 'G' : 'H',
168 HasUMode(target_p, UMODE_BOT) ? "B" : "",
169 HasUMode(target_p, UMODE_SECURE) ? "S" : "",
170 HasUMode(target_p, UMODE_REGISTERED) ? "r" : "",
171 HasUMode(target_p, UMODE_OPER) ? "*" : "",
172 member ? member_get_prefix(member, who->fields || !!HasCap(source_p, CAP_MULTI_PREFIX)) : "");
173
174 else
175 snprintf(status, sizeof(status), "%c%s%s%s%s%s", target_p->away[0] ? 'G' : 'H',
176 HasUMode(target_p, UMODE_BOT) ? "B" : "",
177 HasUMode(target_p, UMODE_SECURE) ? "S" : "",
178 HasUMode(target_p, UMODE_REGISTERED) ? "r" : "",
179 HasUMode(target_p, UMODE_OPER) &&
180 !HasUMode(target_p, UMODE_HIDDEN) ? "*" : "",
181 member ? member_get_prefix(member, who->fields || !!HasCap(source_p, CAP_MULTI_PREFIX)) : "");
182 p += snprintf(p, sizeof(buf) - (p - buf), " %s", status);
183 }
184
185 if (who->fields == 0 || (who->fields & WHO_FIELD_DIS))
186 {
187 if (!HasUMode(source_p, UMODE_OPER) &&
188 (ConfigServerHide.hide_servers || IsHidden(target_p->servptr)))
189 p += snprintf(p, sizeof(buf) - (p - buf), " %s%u", who->fields == 0 ? ":" : "", 0);
190 else
191 p += snprintf(p, sizeof(buf) - (p - buf), " %s%u", who->fields == 0 ? ":" : "", target_p->hopcount);
192 }
193
194 if ((who->fields & WHO_FIELD_IDL))
195 {
196 if (MyClient(target_p) &&
197 (HasUMode(source_p, UMODE_OPER) || target_p == source_p))
198 p += snprintf(p, sizeof(buf) - (p - buf), " %u", client_get_idle_time(source_p, target_p));
199 else
200 p += snprintf(p, sizeof(buf) - (p - buf), " %u", 0);
201 }
202
203 if ((who->fields & WHO_FIELD_ACC))
204 {
205 if (strcmp(target_p->account, "*"))
206 p += snprintf(p, sizeof(buf) - (p - buf), " %s", target_p->account);
207 else
208 p += snprintf(p, sizeof(buf) - (p - buf), " %s", "0");
209 }
210
211 if ((who->fields & WHO_FIELD_OPL))
212 p += snprintf(p, sizeof(buf) - (p - buf), " %s", "n/a");
213
214 if (who->fields == 0 || (who->fields & WHO_FIELD_REN))
215 p += snprintf(p, sizeof(buf) - (p - buf), " %s%s", who->fields ? ":" : "", target_p->info);
216 /* Place colon here for special reply ^ */
217
218 sendto_one_numeric(source_p, &me, who->fields ? RPL_WHOSPCRPL : RPL_WHOREPLY, buf + 1);
219 }
220
221 /*!
222 * \param source_p Pointer to client requesting who.
223 * \param target_p Pointer to client to do who on.
224 * \param mask Mask to match.
225 * \param who Pointer to struct WhoQuery item that defines the options for this query.
226 * \return true if mask matches, false otherwise.
227 */
228 static bool
229 who_matches(struct Client *source_p, const struct Client *target_p,
230 const char *mask, const struct WhoQuery *who)
231 {
232 if ((who->bitsel & WHOSELECT_OPER))
233 if (!HasUMode(target_p, UMODE_OPER) ||
234 (HasUMode(target_p, UMODE_HIDDEN) && !HasUMode(source_p, UMODE_OPER)))
235 return false;
236
237 if (mask == NULL)
238 return true;
239
240 if ((who->matchsel & WHO_FIELD_NIC) && match(mask, target_p->name) == 0)
241 return true;
242
243 if ((who->matchsel & WHO_FIELD_UID) && match(mask, target_p->username) == 0)
244 return true;
245
246 if ((who->matchsel & WHO_FIELD_HOS))
247 {
248 if (match(mask, target_p->host) == 0)
249 return true;
250 else if (HasUMode(source_p, UMODE_OPER) && match(mask, target_p->realhost) == 0)
251 return true;
252 }
253
254 if ((who->matchsel & WHO_FIELD_REN) && match(mask, target_p->info) == 0)
255 return true;
256
257 if ((who->matchsel & WHO_FIELD_ACC) && match(mask, target_p->account) == 0)
258 return true;
259
260 if ((who->matchsel & WHO_FIELD_NIP) && HasUMode(source_p, UMODE_OPER))
261 {
262 struct irc_ssaddr addr;
263 int bits = 0;
264 const int ret = parse_netmask(mask, &addr, &bits);
265
266 if (ret == HM_IPV4 || ret == HM_IPV6)
267 if (address_compare(&target_p->ip, &addr, false, false, bits) == true)
268 return true;
269
270 if (match(mask, target_p->sockhost) == 0)
271 return true;
272 }
273
274 if ((who->matchsel & WHO_FIELD_SER))
275 if (HasUMode(source_p, UMODE_OPER) ||
276 (ConfigServerHide.hide_servers == 0 && !IsHidden(target_p->servptr)))
277 if (match(mask, target_p->servptr->name) == 0)
278 return true;
279
280 return false;
281 }
282
283 /*! \brief Lists matching clients on specified channel. Marks matched clients.
284 * \param source_p Pointer to client requesting who.
285 * \param channel Pointer to channel member chain.
286 * \param mask Mask to match.
287 * \param who Pointer to struct WhoQuery item that defines the options for this query.
288 */
289 static void
290 who_on_common_channel(struct Client *source_p, struct Channel *channel, const char *mask,
291 struct WhoQuery *who)
292 {
293 dlink_node *node;
294
295 DLINK_FOREACH(node, channel->members.head)
296 {
297 struct ChannelMember *member = node->data;
298 struct Client *target_p = member->client;
299
300 if (!HasUMode(target_p, UMODE_INVISIBLE) || HasFlag(target_p, FLAGS_MARK))
301 continue;
302
303 AddFlag(target_p, FLAGS_MARK);
304
305 if (who->maxmatches)
306 {
307 if (who_matches(source_p, target_p, mask, who) == true)
308 {
309 who_send(source_p, target_p, member, who);
310 --who->maxmatches;
311 }
312 }
313 }
314 }
315
316 /*! \brief Does a global scan of all clients looking for match.
317 * \param source_p Pointer to client requesting who.
318 * \param mask Mask to match.
319 * \param who Pointer to struct WhoQuery item that defines the options for this query.
320 */
321 static void
322 who_global(struct Client *source_p, const char *mask, struct WhoQuery *who)
323 {
324 dlink_node *node;
325 static uintmax_t last_used = 0;
326
327 if (!HasUMode(source_p, UMODE_OPER))
328 {
329 if ((last_used + ConfigGeneral.pace_wait) > event_base->time.sec_monotonic)
330 {
331 sendto_one_numeric(source_p, &me, RPL_LOAD2HI, "WHO");
332 return;
333 }
334
335 last_used = event_base->time.sec_monotonic;
336 }
337
338 /* First, list all matching invisible clients on common channels */
339 DLINK_FOREACH(node, source_p->channel.head)
340 {
341 struct ChannelMember *member = node->data;
342 who_on_common_channel(source_p, member->channel, mask, who);
343 }
344
345 /* Second, list all matching visible clients */
346 DLINK_FOREACH(node, global_client_list.head)
347 {
348 struct Client *target_p = node->data;
349
350 assert(IsClient(target_p));
351
352 if (HasUMode(target_p, UMODE_INVISIBLE))
353 {
354 DelFlag(target_p, FLAGS_MARK);
355 continue;
356 }
357
358 if (who->maxmatches)
359 {
360 if (who_matches(source_p, target_p, mask, who) == true)
361 {
362 who_send(source_p, target_p, NULL, who);
363 --who->maxmatches;
364 }
365 }
366 }
367 }
368
369 /*! \brief Does a WHO on given channel.
370 * \param source_p Pointer to client requesting who.
371 * \param channel Pointer to channel to do who on.
372 * \param who Pointer to struct WhoQuery item that defines the options for this query.
373 */
374 static void
375 who_on_channel(struct Client *source_p, struct Channel *channel, const struct WhoQuery *who)
376 {
377 bool is_member = false;
378
379 if (HasUMode(source_p, UMODE_ADMIN) || member_find_link(source_p, channel))
380 is_member = true;
381 else if (SecretChannel(channel))
382 return;
383
384 dlink_node *node;
385 DLINK_FOREACH(node, channel->members.head)
386 {
387 struct ChannelMember *member = node->data;
388 struct Client *target_p = member->client;
389
390 if (is_member == true || !HasUMode(target_p, UMODE_INVISIBLE))
391 {
392 if ((who->bitsel & WHOSELECT_OPER))
393 if (!HasUMode(target_p, UMODE_OPER) ||
394 (HasUMode(target_p, UMODE_HIDDEN) && !HasUMode(source_p, UMODE_OPER)))
395 continue;
396
397 who_send(source_p, target_p, member, who);
398 }
399 }
400 }
401
402 /*! \brief WHO command handler
403 *
404 * \param source_p Pointer to allocated Client struct from which the message
405 * originally comes from. This can be a local or remote client.
406 * \param parc Integer holding the number of supplied arguments.
407 * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL
408 * pointers.
409 * \note Valid arguments for this command are:
410 * - parv[0] = command
411 * - parv[1] = mask
412 * - parv[2] = additional selection flag, only 'o' for now.
413 * and %flags to specify what fields to output
414 * plus a ,querytype if the t flag is specified
415 * so the final thing will be like o%tnchu,777
416 */
417 static void
418 m_who(struct Client *source_p, int parc, char *parv[])
419 {
420 char *mask = parv[1];
421 char *options = parv[2];
422 char *token = NULL;
423 struct WhoQuery w = { .maxmatches = WHO_MAX_REPLIES }, *who = &w;
424
425 if (!EmptyString(options))
426 {
427 char ch;
428 char *p = options;
429
430 while ((ch = *p++) && (ch != '%') && (ch != ','))
431 {
432 switch (ch)
433 {
434 case 'o':
435 case 'O':
436 who->bitsel |= WHOSELECT_OPER;
437 break;
438 case 'n':
439 case 'N':
440 who->matchsel |= WHO_FIELD_NIC;
441 break;
442 case 'u':
443 case 'U':
444 who->matchsel |= WHO_FIELD_UID;
445 break;
446 case 'h':
447 case 'H':
448 who->matchsel |= WHO_FIELD_HOS;
449 break;
450 case 'i':
451 case 'I':
452 who->matchsel |= WHO_FIELD_NIP;
453 break;
454 case 's':
455 case 'S':
456 who->matchsel |= WHO_FIELD_SER;
457 break;
458 case 'r':
459 case 'R':
460 who->matchsel |= WHO_FIELD_REN;
461 break;
462 case 'a':
463 case 'A':
464 who->matchsel |= WHO_FIELD_ACC;
465 break;
466 }
467 }
468
469 if (ch == '%')
470 {
471 while ((ch = *p++) && (ch != ','))
472 {
473 switch (ch)
474 {
475 case 'c':
476 case 'C':
477 who->fields |= WHO_FIELD_CHA;
478 break;
479 case 'd':
480 case 'D':
481 who->fields |= WHO_FIELD_DIS;
482 break;
483 case 'f':
484 case 'F':
485 who->fields |= WHO_FIELD_FLA;
486 break;
487 case 'h':
488 case 'H':
489 who->fields |= WHO_FIELD_HOS;
490 break;
491 case 'i':
492 case 'I':
493 who->fields |= WHO_FIELD_NIP;
494 break;
495 case 'l':
496 case 'L':
497 who->fields |= WHO_FIELD_IDL;
498 break;
499 case 'n':
500 case 'N':
501 who->fields |= WHO_FIELD_NIC;
502 break;
503 case 'r':
504 case 'R':
505 who->fields |= WHO_FIELD_REN;
506 break;
507 case 's':
508 case 'S':
509 who->fields |= WHO_FIELD_SER;
510 break;
511 case 't':
512 case 'T':
513 who->fields |= WHO_FIELD_QTO;
514 break;
515 case 'u':
516 case 'U':
517 who->fields |= WHO_FIELD_UID;
518 break;
519 case 'a':
520 case 'A':
521 who->fields |= WHO_FIELD_ACC;
522 break;
523 case 'o':
524 case 'O':
525 who->fields |= WHO_FIELD_OPL;
526 break;
527 }
528 }
529 }
530
531 if (ch)
532 token = p;
533
534 if (token && (who->fields & WHO_FIELD_QTO))
535 {
536 p = token;
537 if (!((*p > '9') || (*p < '0')))
538 p++;
539 if (!((*p > '9') || (*p < '0')))
540 p++;
541 if (!((*p > '9') || (*p < '0')))
542 p++;
543 *p = '\0';
544 }
545 else
546 token = NULL;
547
548 who->token = token;
549 }
550
551 if (who->matchsel == 0)
552 who->matchsel = WHO_FIELD_DEF;
553
554 /* '/who #some_channel' */
555 if (IsChanPrefix(*mask))
556 {
557 /* List all users on a given channel */
558 struct Channel *channel = hash_find_channel(mask);
559 if (channel)
560 who_on_channel(source_p, channel, who);
561
562 sendto_one_numeric(source_p, &me, RPL_ENDOFWHO, mask);
563 return;
564 }
565
566 collapse(mask);
567 if (strcmp(mask, "0") == 0 ||
568 strcmp(mask, "*") == 0)
569 mask = NULL;
570
571 who_global(source_p, mask, who);
572
573 if (who->maxmatches == 0)
574 sendto_one_numeric(source_p, &me, ERR_WHOLIMEXCEED, WHO_MAX_REPLIES, "WHO");
575 sendto_one_numeric(source_p, &me, RPL_ENDOFWHO, EmptyString(mask) ? "*" : mask);
576 }
577
578 static struct Message who_msgtab =
579 {
580 .cmd = "WHO",
581 .handlers[UNREGISTERED_HANDLER] = { .handler = m_unregistered },
582 .handlers[CLIENT_HANDLER] = { .handler = m_who, .args_min = 2 },
583 .handlers[SERVER_HANDLER] = { .handler = m_ignore },
584 .handlers[ENCAP_HANDLER] = { .handler = m_ignore },
585 .handlers[OPER_HANDLER] = { .handler = m_who, .args_min = 2 }
586 };
587
588 static void
589 module_init(void)
590 {
591 mod_add_cmd(&who_msgtab);
592 isupport_add("WHOX", NULL, -1);
593 }
594
595 static void
596 module_exit(void)
597 {
598 mod_del_cmd(&who_msgtab);
599 isupport_delete("WHOX");
600 }
601
602 struct module module_entry =
603 {
604 .version = "$Revision$",
605 .modinit = module_init,
606 .modexit = module_exit,
607 };

Properties

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