]>
Commit | Line | Data |
---|---|---|
212380e3 AC |
1 | /* |
2 | * ircd-ratbox: A slightly useful ircd. | |
3 | * m_who.c: Shows who is on a channel. | |
4 | * | |
5 | * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center | |
6 | * Copyright (C) 1996-2002 Hybrid Development Team | |
7 | * Copyright (C) 2002-2005 ircd-ratbox development team | |
8 | * | |
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 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
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. | |
18 | * | |
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
22 | * USA | |
212380e3 AC |
23 | */ |
24 | #include "stdinc.h" | |
212380e3 AC |
25 | #include "client.h" |
26 | #include "channel.h" | |
27 | #include "hash.h" | |
28 | #include "ircd.h" | |
29 | #include "numeric.h" | |
30 | #include "s_serv.h" | |
31 | #include "send.h" | |
4562c604 | 32 | #include "match.h" |
212380e3 | 33 | #include "s_conf.h" |
4016731b | 34 | #include "logger.h" |
212380e3 AC |
35 | #include "msg.h" |
36 | #include "parse.h" | |
37 | #include "modules.h" | |
38 | #include "packet.h" | |
39 | #include "s_newconf.h" | |
7e132ff0 | 40 | #include "ratelimit.h" |
0b904d91 | 41 | #include "supported.h" |
212380e3 | 42 | |
48957a49 JT |
43 | #define FIELD_CHANNEL 0x0001 |
44 | #define FIELD_HOP 0x0002 | |
45 | #define FIELD_FLAGS 0x0004 | |
46 | #define FIELD_HOST 0x0008 | |
47 | #define FIELD_IP 0x0010 | |
48 | #define FIELD_IDLE 0x0020 | |
49 | #define FIELD_NICK 0x0040 | |
50 | #define FIELD_INFO 0x0080 | |
51 | #define FIELD_SERVER 0x0100 | |
52 | #define FIELD_QUERYTYPE 0x0200 /* cookie for client */ | |
53 | #define FIELD_USER 0x0400 | |
54 | #define FIELD_ACCOUNT 0x0800 | |
55 | #define FIELD_OPLEVEL 0x1000 /* meaningless and stupid, but whatever */ | |
56 | ||
eeabf33a EM |
57 | static const char who_desc[] = |
58 | "Provides the WHO command to display information for users on a channel"; | |
59 | ||
48957a49 JT |
60 | struct who_format |
61 | { | |
62 | int fields; | |
63 | const char *querytype; | |
64 | }; | |
65 | ||
3c7d6fcc EM |
66 | static void m_who(struct MsgBuf *, struct Client *, struct Client *, int, const char **); |
67 | ||
68 | static void do_who_on_channel(struct Client *source_p, struct Channel *chptr, | |
69 | int server_oper, int member, | |
70 | struct who_format *fmt); | |
71 | static void who_global(struct Client *source_p, const char *mask, int server_oper, int operspy, struct who_format *fmt); | |
72 | static void do_who(struct Client *source_p, | |
73 | struct Client *target_p, struct membership *msptr, | |
74 | struct who_format *fmt); | |
212380e3 AC |
75 | |
76 | struct Message who_msgtab = { | |
7baa37a9 | 77 | "WHO", 0, 0, 0, 0, |
212380e3 AC |
78 | {mg_unreg, {m_who, 2}, mg_ignore, mg_ignore, mg_ignore, {m_who, 2}} |
79 | }; | |
80 | ||
0b904d91 AC |
81 | static int |
82 | _modinit(void) | |
83 | { | |
84 | add_isupport("WHOX", isupport_string, ""); | |
85 | return 0; | |
86 | } | |
87 | ||
88 | static void | |
89 | _moddeinit(void) | |
90 | { | |
91 | delete_isupport("WHOX"); | |
92 | } | |
93 | ||
01fb744c DS |
94 | int doing_who_show_idle_hook; |
95 | ||
212380e3 | 96 | mapi_clist_av1 who_clist[] = { &who_msgtab, NULL }; |
01fb744c DS |
97 | mapi_hlist_av1 who_hlist[] = { |
98 | { "doing_who_show_idle", &doing_who_show_idle_hook }, | |
99 | { NULL, NULL } | |
100 | }; | |
101 | DECLARE_MODULE_AV2(who, _modinit, _moddeinit, who_clist, who_hlist, NULL, NULL, NULL, who_desc); | |
212380e3 | 102 | |
212380e3 AC |
103 | /* |
104 | ** m_who | |
212380e3 | 105 | ** parv[1] = nickname mask list |
48957a49 | 106 | ** parv[2] = additional selection flag and format options |
212380e3 | 107 | */ |
3c7d6fcc | 108 | static void |
428ca87b | 109 | m_who(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) |
212380e3 AC |
110 | { |
111 | static time_t last_used = 0; | |
112 | struct Client *target_p; | |
113 | struct membership *msptr; | |
114 | char *mask; | |
5b96d9a6 | 115 | rb_dlink_node *lp; |
212380e3 AC |
116 | struct Channel *chptr = NULL; |
117 | int server_oper = parc > 2 ? (*parv[2] == 'o') : 0; /* Show OPERS only */ | |
118 | int member; | |
119 | int operspy = 0; | |
48957a49 JT |
120 | struct who_format fmt; |
121 | const char *s; | |
04d77684 | 122 | char maskcopy[512]; |
48957a49 JT |
123 | |
124 | fmt.fields = 0; | |
125 | fmt.querytype = NULL; | |
126 | if (parc > 2 && (s = strchr(parv[2], '%')) != NULL) | |
127 | { | |
128 | s++; | |
129 | for (; *s != '\0'; s++) | |
130 | { | |
131 | switch (*s) | |
132 | { | |
133 | case 'c': fmt.fields |= FIELD_CHANNEL; break; | |
134 | case 'd': fmt.fields |= FIELD_HOP; break; | |
135 | case 'f': fmt.fields |= FIELD_FLAGS; break; | |
136 | case 'h': fmt.fields |= FIELD_HOST; break; | |
137 | case 'i': fmt.fields |= FIELD_IP; break; | |
138 | case 'l': fmt.fields |= FIELD_IDLE; break; | |
139 | case 'n': fmt.fields |= FIELD_NICK; break; | |
140 | case 'r': fmt.fields |= FIELD_INFO; break; | |
141 | case 's': fmt.fields |= FIELD_SERVER; break; | |
142 | case 't': fmt.fields |= FIELD_QUERYTYPE; break; | |
143 | case 'u': fmt.fields |= FIELD_USER; break; | |
144 | case 'a': fmt.fields |= FIELD_ACCOUNT; break; | |
145 | case 'o': fmt.fields |= FIELD_OPLEVEL; break; | |
146 | case ',': | |
147 | s++; | |
148 | fmt.querytype = s; | |
149 | s += strlen(s); | |
150 | s--; | |
151 | break; | |
152 | } | |
153 | } | |
154 | if (EmptyString(fmt.querytype) || strlen(fmt.querytype) > 3) | |
155 | fmt.querytype = "0"; | |
156 | } | |
212380e3 | 157 | |
62656efd | 158 | rb_strlcpy(maskcopy, parv[1], sizeof maskcopy); |
04d77684 | 159 | mask = maskcopy; |
212380e3 AC |
160 | |
161 | collapse(mask); | |
162 | ||
163 | /* '/who *' */ | |
164 | if((*(mask + 1) == '\0') && (*mask == '*')) | |
165 | { | |
166 | if(source_p->user == NULL) | |
3c7d6fcc | 167 | return; |
212380e3 AC |
168 | |
169 | if((lp = source_p->user->channel.head) != NULL) | |
170 | { | |
171 | msptr = lp->data; | |
3c7d6fcc | 172 | do_who_on_channel(source_p, msptr->chptr, server_oper, true, &fmt); |
212380e3 AC |
173 | } |
174 | ||
175 | sendto_one(source_p, form_str(RPL_ENDOFWHO), | |
176 | me.name, source_p->name, "*"); | |
3c7d6fcc | 177 | return; |
212380e3 AC |
178 | } |
179 | ||
180 | if(IsOperSpy(source_p) && *mask == '!') | |
181 | { | |
182 | mask++; | |
183 | operspy = 1; | |
184 | ||
185 | if(EmptyString(mask)) | |
186 | { | |
187 | sendto_one(source_p, form_str(RPL_ENDOFWHO), | |
188 | me.name, source_p->name, parv[1]); | |
3c7d6fcc | 189 | return; |
212380e3 AC |
190 | } |
191 | } | |
192 | ||
193 | /* '/who #some_channel' */ | |
194 | if(IsChannelName(mask)) | |
195 | { | |
196 | /* List all users on a given channel */ | |
f71e18ee | 197 | chptr = find_channel(parv[1] + operspy); |
7e132ff0 | 198 | |
212380e3 AC |
199 | if(chptr != NULL) |
200 | { | |
d4f7eb4c | 201 | if (!IsOperGeneral(source_p) && !ratelimit_client_who(source_p, rb_dlink_list_length(&chptr->members)/50)) |
7e132ff0 KB |
202 | { |
203 | sendto_one(source_p, form_str(RPL_LOAD2HI), | |
204 | me.name, source_p->name, "WHO"); | |
205 | sendto_one(source_p, form_str(RPL_ENDOFWHO), | |
206 | me.name, source_p->name, "*"); | |
3c7d6fcc | 207 | return; |
7e132ff0 KB |
208 | } |
209 | ||
212380e3 AC |
210 | if(operspy) |
211 | report_operspy(source_p, "WHO", chptr->chname); | |
212 | ||
213 | if(IsMember(source_p, chptr) || operspy) | |
3c7d6fcc | 214 | do_who_on_channel(source_p, chptr, server_oper, true, &fmt); |
212380e3 | 215 | else if(!SecretChannel(chptr)) |
3c7d6fcc | 216 | do_who_on_channel(source_p, chptr, server_oper, false, &fmt); |
212380e3 | 217 | } |
7e132ff0 | 218 | |
212380e3 | 219 | sendto_one(source_p, form_str(RPL_ENDOFWHO), |
f71e18ee | 220 | me.name, source_p->name, parv[1] + operspy); |
3c7d6fcc | 221 | return; |
212380e3 AC |
222 | } |
223 | ||
224 | /* '/who nick' */ | |
225 | ||
226 | if(((target_p = find_named_person(mask)) != NULL) && | |
1123eefc | 227 | (!server_oper || SeesOper(target_p, source_p))) |
212380e3 AC |
228 | { |
229 | int isinvis = 0; | |
230 | ||
231 | isinvis = IsInvisible(target_p); | |
5b96d9a6 | 232 | RB_DLINK_FOREACH(lp, target_p->user->channel.head) |
212380e3 AC |
233 | { |
234 | msptr = lp->data; | |
235 | chptr = msptr->chptr; | |
236 | ||
237 | member = IsMember(source_p, chptr); | |
238 | ||
239 | if(isinvis && !member) | |
240 | continue; | |
241 | ||
242 | if(member || (!isinvis && PubChannel(chptr))) | |
243 | break; | |
244 | } | |
245 | ||
246 | /* if we stopped midlist, lp->data is the membership for | |
247 | * target_p of chptr | |
248 | */ | |
249 | if(lp != NULL) | |
02eca3f1 | 250 | do_who(source_p, target_p, lp->data, &fmt); |
212380e3 | 251 | else |
02eca3f1 | 252 | do_who(source_p, target_p, NULL, &fmt); |
212380e3 | 253 | |
55abcbb2 | 254 | sendto_one(source_p, form_str(RPL_ENDOFWHO), |
212380e3 | 255 | me.name, source_p->name, mask); |
3c7d6fcc | 256 | return; |
212380e3 AC |
257 | } |
258 | ||
259 | if(!IsFloodDone(source_p)) | |
260 | flood_endgrace(source_p); | |
261 | ||
262 | /* it has to be a global who at this point, limit it */ | |
d4f7eb4c | 263 | if(!IsOperGeneral(source_p)) |
212380e3 | 264 | { |
7e132ff0 | 265 | if((last_used + ConfigFileEntry.pace_wait) > rb_current_time() || !ratelimit_client(source_p, 1)) |
212380e3 AC |
266 | { |
267 | sendto_one(source_p, form_str(RPL_LOAD2HI), | |
268 | me.name, source_p->name, "WHO"); | |
269 | sendto_one(source_p, form_str(RPL_ENDOFWHO), | |
270 | me.name, source_p->name, "*"); | |
3c7d6fcc | 271 | return; |
212380e3 AC |
272 | } |
273 | else | |
e3354945 | 274 | last_used = rb_current_time(); |
212380e3 AC |
275 | } |
276 | ||
277 | /* Note: operspy_dont_care_user_info does not apply to | |
278 | * who on channels */ | |
279 | if(IsOperSpy(source_p) && ConfigFileEntry.operspy_dont_care_user_info) | |
280 | operspy = 1; | |
281 | ||
282 | /* '/who 0' for a global list. this forces clients to actually | |
283 | * request a full list. I presume its because of too many typos | |
284 | * with "/who" ;) --fl | |
285 | */ | |
286 | if((*(mask + 1) == '\0') && (*mask == '0')) | |
48957a49 | 287 | who_global(source_p, NULL, server_oper, 0, &fmt); |
212380e3 | 288 | else |
48957a49 | 289 | who_global(source_p, mask, server_oper, operspy, &fmt); |
212380e3 AC |
290 | |
291 | sendto_one(source_p, form_str(RPL_ENDOFWHO), | |
292 | me.name, source_p->name, mask); | |
212380e3 AC |
293 | } |
294 | ||
295 | /* who_common_channel | |
296 | * inputs - pointer to client requesting who | |
297 | * - pointer to channel member chain. | |
298 | * - char * mask to match | |
299 | * - int if oper on a server or not | |
300 | * - pointer to int maxmatches | |
48957a49 | 301 | * - format options |
212380e3 AC |
302 | * output - NONE |
303 | * side effects - lists matching invisible clients on specified channel, | |
304 | * marks matched clients. | |
305 | */ | |
306 | static void | |
307 | who_common_channel(struct Client *source_p, struct Channel *chptr, | |
48957a49 JT |
308 | const char *mask, int server_oper, int *maxmatches, |
309 | struct who_format *fmt) | |
212380e3 AC |
310 | { |
311 | struct membership *msptr; | |
312 | struct Client *target_p; | |
5b96d9a6 | 313 | rb_dlink_node *ptr; |
212380e3 | 314 | |
5b96d9a6 | 315 | RB_DLINK_FOREACH(ptr, chptr->members.head) |
212380e3 AC |
316 | { |
317 | msptr = ptr->data; | |
318 | target_p = msptr->client_p; | |
319 | ||
320 | if(!IsInvisible(target_p) || IsMarked(target_p)) | |
321 | continue; | |
322 | ||
1123eefc | 323 | if(server_oper && !SeesOper(target_p, source_p)) |
212380e3 AC |
324 | continue; |
325 | ||
326 | SetMark(target_p); | |
327 | ||
328 | if(*maxmatches > 0) | |
329 | { | |
330 | if((mask == NULL) || | |
331 | match(mask, target_p->name) || match(mask, target_p->username) || | |
c88cdb00 | 332 | match(mask, target_p->host) || match(mask, target_p->servptr->name) || |
d4f7eb4c | 333 | (IsOperGeneral(source_p) && match(mask, target_p->orighost)) || |
212380e3 AC |
334 | match(mask, target_p->info)) |
335 | { | |
02eca3f1 | 336 | do_who(source_p, target_p, NULL, fmt); |
212380e3 AC |
337 | --(*maxmatches); |
338 | } | |
339 | } | |
340 | } | |
341 | } | |
342 | ||
343 | /* | |
344 | * who_global | |
345 | * | |
346 | * inputs - pointer to client requesting who | |
347 | * - char * mask to match | |
348 | * - int if oper on a server or not | |
48957a49 JT |
349 | * - int if operspy or not |
350 | * - format options | |
212380e3 AC |
351 | * output - NONE |
352 | * side effects - do a global scan of all clients looking for match | |
353 | * this is slightly expensive on EFnet ... | |
354 | * marks assumed cleared for all clients initially | |
355 | * and will be left cleared on return | |
356 | */ | |
357 | static void | |
48957a49 | 358 | who_global(struct Client *source_p, const char *mask, int server_oper, int operspy, struct who_format *fmt) |
212380e3 AC |
359 | { |
360 | struct membership *msptr; | |
361 | struct Client *target_p; | |
5b96d9a6 | 362 | rb_dlink_node *lp, *ptr; |
212380e3 AC |
363 | int maxmatches = 500; |
364 | ||
365 | /* first, list all matching INvisible clients on common channels | |
366 | * if this is not an operspy who | |
367 | */ | |
368 | if(!operspy) | |
369 | { | |
5b96d9a6 | 370 | RB_DLINK_FOREACH(lp, source_p->user->channel.head) |
212380e3 AC |
371 | { |
372 | msptr = lp->data; | |
48957a49 | 373 | who_common_channel(source_p, msptr->chptr, mask, server_oper, &maxmatches, fmt); |
212380e3 AC |
374 | } |
375 | } | |
8e239de8 JP |
376 | else |
377 | { | |
378 | maxmatches = INT_MAX; | |
379 | if (!ConfigFileEntry.operspy_dont_care_user_info) | |
380 | report_operspy(source_p, "WHO", mask); | |
381 | } | |
212380e3 AC |
382 | |
383 | /* second, list all matching visible clients and clear all marks | |
384 | * on invisible clients | |
385 | * if this is an operspy who, list all matching clients, no need | |
386 | * to clear marks | |
387 | */ | |
5b96d9a6 | 388 | RB_DLINK_FOREACH(ptr, global_client_list.head) |
212380e3 AC |
389 | { |
390 | target_p = ptr->data; | |
391 | if(!IsPerson(target_p)) | |
392 | continue; | |
393 | ||
394 | if(IsInvisible(target_p) && !operspy) | |
395 | { | |
396 | ClearMark(target_p); | |
397 | continue; | |
398 | } | |
399 | ||
1123eefc | 400 | if(server_oper && !SeesOper(target_p, source_p)) |
212380e3 AC |
401 | continue; |
402 | ||
403 | if(maxmatches > 0) | |
404 | { | |
405 | if(!mask || | |
406 | match(mask, target_p->name) || match(mask, target_p->username) || | |
c88cdb00 | 407 | match(mask, target_p->host) || match(mask, target_p->servptr->name) || |
d4f7eb4c | 408 | (IsOperGeneral(source_p) && match(mask, target_p->orighost)) || |
212380e3 AC |
409 | match(mask, target_p->info)) |
410 | { | |
02eca3f1 | 411 | do_who(source_p, target_p, NULL, fmt); |
212380e3 AC |
412 | --maxmatches; |
413 | } | |
414 | } | |
415 | } | |
416 | ||
417 | if (maxmatches <= 0) | |
418 | sendto_one(source_p, | |
419 | form_str(ERR_TOOMANYMATCHES), | |
420 | me.name, source_p->name, "WHO"); | |
421 | } | |
422 | ||
423 | /* | |
424 | * do_who_on_channel | |
425 | * | |
426 | * inputs - pointer to client requesting who | |
427 | * - pointer to channel to do who on | |
428 | * - The "real name" of this channel | |
429 | * - int if source_p is a server oper or not | |
430 | * - int if client is member or not | |
48957a49 | 431 | * - format options |
212380e3 AC |
432 | * output - NONE |
433 | * side effects - do a who on given channel | |
434 | */ | |
435 | static void | |
436 | do_who_on_channel(struct Client *source_p, struct Channel *chptr, | |
48957a49 | 437 | int server_oper, int member, struct who_format *fmt) |
212380e3 AC |
438 | { |
439 | struct Client *target_p; | |
440 | struct membership *msptr; | |
5b96d9a6 | 441 | rb_dlink_node *ptr; |
212380e3 | 442 | |
5b96d9a6 | 443 | RB_DLINK_FOREACH(ptr, chptr->members.head) |
212380e3 AC |
444 | { |
445 | msptr = ptr->data; | |
446 | target_p = msptr->client_p; | |
447 | ||
1123eefc | 448 | if(server_oper && !SeesOper(target_p, source_p)) |
212380e3 AC |
449 | continue; |
450 | ||
451 | if(member || !IsInvisible(target_p)) | |
02eca3f1 | 452 | do_who(source_p, target_p, msptr, fmt); |
212380e3 AC |
453 | } |
454 | } | |
455 | ||
bac250f6 JT |
456 | /* |
457 | * append_format | |
458 | * | |
459 | * inputs - pointer to buffer | |
460 | * - size of buffer | |
461 | * - pointer to position | |
462 | * - format string | |
463 | * - arguments for format | |
464 | * output - NONE | |
465 | * side effects - position incremented, possibly beyond size of buffer | |
466 | * this allows detecting overflow | |
467 | */ | |
468 | static void | |
469 | append_format(char *buf, size_t bufsize, size_t *pos, const char *fmt, ...) | |
470 | { | |
471 | size_t max, result; | |
472 | va_list ap; | |
473 | ||
474 | max = *pos >= bufsize ? 0 : bufsize - *pos; | |
475 | va_start(ap, fmt); | |
5203cba5 | 476 | result = vsnprintf(buf + *pos, max, fmt, ap); |
bac250f6 JT |
477 | va_end(ap); |
478 | *pos += result; | |
479 | } | |
480 | ||
212380e3 AC |
481 | /* |
482 | * do_who | |
483 | * | |
484 | * inputs - pointer to client requesting who | |
485 | * - pointer to client to do who on | |
02eca3f1 | 486 | * - channel membership or NULL |
48957a49 | 487 | * - format options |
212380e3 AC |
488 | * output - NONE |
489 | * side effects - do a who on given person | |
490 | */ | |
491 | ||
492 | static void | |
02eca3f1 | 493 | do_who(struct Client *source_p, struct Client *target_p, struct membership *msptr, struct who_format *fmt) |
212380e3 | 494 | { |
ea6dade2 | 495 | char status[16]; |
bac250f6 JT |
496 | char str[510 + 1]; /* linebuf.c will add \r\n */ |
497 | size_t pos; | |
48957a49 | 498 | const char *q; |
212380e3 | 499 | |
5203cba5 | 500 | sprintf(status, "%c%s%s", |
1123eefc | 501 | target_p->user->away ? 'G' : 'H', SeesOper(target_p, source_p) ? "*" : "", msptr ? find_channel_status(msptr, fmt->fields || IsCapable(source_p, CLICAP_MULTI_PREFIX)) : ""); |
212380e3 | 502 | |
48957a49 JT |
503 | if (fmt->fields == 0) |
504 | sendto_one(source_p, form_str(RPL_WHOREPLY), me.name, | |
02eca3f1 | 505 | source_p->name, msptr ? msptr->chptr->chname : "*", |
48957a49 JT |
506 | target_p->username, target_p->host, |
507 | target_p->servptr->name, target_p->name, status, | |
d4f7eb4c | 508 | ConfigServerHide.flatten_links && !IsOperGeneral(source_p) && !IsExemptShide(source_p) ? 0 : target_p->hopcount, |
48957a49 JT |
509 | target_p->info); |
510 | else | |
511 | { | |
512 | str[0] = '\0'; | |
bac250f6 JT |
513 | pos = 0; |
514 | append_format(str, sizeof str, &pos, ":%s %d %s", | |
515 | me.name, RPL_WHOSPCRPL, source_p->name); | |
48957a49 | 516 | if (fmt->fields & FIELD_QUERYTYPE) |
bac250f6 | 517 | append_format(str, sizeof str, &pos, " %s", fmt->querytype); |
48957a49 | 518 | if (fmt->fields & FIELD_CHANNEL) |
bac250f6 | 519 | append_format(str, sizeof str, &pos, " %s", msptr ? msptr->chptr->chname : "*"); |
48957a49 | 520 | if (fmt->fields & FIELD_USER) |
bac250f6 | 521 | append_format(str, sizeof str, &pos, " %s", target_p->username); |
48957a49 JT |
522 | if (fmt->fields & FIELD_IP) |
523 | { | |
524 | if (show_ip(source_p, target_p) && !EmptyString(target_p->sockhost) && strcmp(target_p->sockhost, "0")) | |
bac250f6 | 525 | append_format(str, sizeof str, &pos, " %s", target_p->sockhost); |
48957a49 | 526 | else |
bac250f6 | 527 | append_format(str, sizeof str, &pos, " %s", "255.255.255.255"); |
48957a49 JT |
528 | } |
529 | if (fmt->fields & FIELD_HOST) | |
bac250f6 | 530 | append_format(str, sizeof str, &pos, " %s", target_p->host); |
48957a49 | 531 | if (fmt->fields & FIELD_SERVER) |
bac250f6 | 532 | append_format(str, sizeof str, &pos, " %s", target_p->servptr->name); |
48957a49 | 533 | if (fmt->fields & FIELD_NICK) |
bac250f6 | 534 | append_format(str, sizeof str, &pos, " %s", target_p->name); |
48957a49 | 535 | if (fmt->fields & FIELD_FLAGS) |
bac250f6 | 536 | append_format(str, sizeof str, &pos, " %s", status); |
48957a49 | 537 | if (fmt->fields & FIELD_HOP) |
d4f7eb4c | 538 | append_format(str, sizeof str, &pos, " %d", ConfigServerHide.flatten_links && !IsOperGeneral(source_p) && !IsExemptShide(source_p) ? 0 : target_p->hopcount); |
48957a49 | 539 | if (fmt->fields & FIELD_IDLE) |
01fb744c DS |
540 | { |
541 | /* fire the doing_who_show_idle hook to allow modules to tell us whether to show the idle time */ | |
542 | hook_data_client_approval hdata_showidle; | |
543 | ||
544 | hdata_showidle.client = source_p; | |
545 | hdata_showidle.target = target_p; | |
546 | hdata_showidle.approved = WHOIS_IDLE_SHOW; | |
547 | ||
548 | call_hook(doing_who_show_idle_hook, &hdata_showidle); | |
549 | ||
550 | append_format(str, sizeof str, &pos, " %d", | |
551 | hdata_showidle.approved ? (int)(MyClient(target_p) ? rb_current_time() - target_p->localClient->last : 0) : 0); | |
552 | } | |
48957a49 JT |
553 | if (fmt->fields & FIELD_ACCOUNT) |
554 | { | |
555 | /* display as in whois */ | |
556 | q = target_p->user->suser; | |
557 | if (!EmptyString(q)) | |
558 | { | |
559 | while(IsDigit(*q)) | |
560 | q++; | |
561 | if(*q == '\0') | |
562 | q = target_p->user->suser; | |
563 | } | |
564 | else | |
565 | q = "0"; | |
bac250f6 | 566 | append_format(str, sizeof str, &pos, " %s", q); |
48957a49 JT |
567 | } |
568 | if (fmt->fields & FIELD_OPLEVEL) | |
bac250f6 | 569 | append_format(str, sizeof str, &pos, " %s", is_chanop(msptr) ? "999" : "n/a"); |
48957a49 | 570 | if (fmt->fields & FIELD_INFO) |
bac250f6 JT |
571 | append_format(str, sizeof str, &pos, " :%s", target_p->info); |
572 | ||
573 | if (pos >= sizeof str) | |
574 | { | |
3c7d6fcc | 575 | static bool warned = false; |
bac250f6 JT |
576 | if (!warned) |
577 | sendto_realops_snomask(SNO_DEBUG, L_NETWIDE, | |
578 | "WHOX overflow while sending information about %s to %s", | |
579 | target_p->name, source_p->name); | |
3c7d6fcc | 580 | warned = true; |
bac250f6 JT |
581 | } |
582 | sendto_one(source_p, "%s", str); | |
48957a49 | 583 | } |
212380e3 | 584 | } |