2 * ircd-ratbox: A slightly useful ircd.
3 * m_who.c: Shows who is on a channel.
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
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
24 * $Id: m_who.c 3350 2007-04-02 22:03:08Z jilles $
42 #include "s_newconf.h"
44 #define FIELD_CHANNEL 0x0001
45 #define FIELD_HOP 0x0002
46 #define FIELD_FLAGS 0x0004
47 #define FIELD_HOST 0x0008
48 #define FIELD_IP 0x0010
49 #define FIELD_IDLE 0x0020
50 #define FIELD_NICK 0x0040
51 #define FIELD_INFO 0x0080
52 #define FIELD_SERVER 0x0100
53 #define FIELD_QUERYTYPE 0x0200 /* cookie for client */
54 #define FIELD_USER 0x0400
55 #define FIELD_ACCOUNT 0x0800
56 #define FIELD_OPLEVEL 0x1000 /* meaningless and stupid, but whatever */
61 const char *querytype
;
64 static int m_who(struct Client
*, struct Client
*, int, const char **);
66 struct Message who_msgtab
= {
67 "WHO", 0, 0, 0, MFLG_SLOW
,
68 {mg_unreg
, {m_who
, 2}, mg_ignore
, mg_ignore
, mg_ignore
, {m_who
, 2}}
71 mapi_clist_av1 who_clist
[] = { &who_msgtab
, NULL
};
72 DECLARE_MODULE_AV1(who
, NULL
, NULL
, who_clist
, NULL
, NULL
, "$Revision: 3350 $");
74 static void do_who_on_channel(struct Client
*source_p
, struct Channel
*chptr
,
75 int server_oper
, int member
,
76 struct who_format
*fmt
);
78 static void who_global(struct Client
*source_p
, const char *mask
, int server_oper
, int operspy
, struct who_format
*fmt
);
80 static void do_who(struct Client
*source_p
,
81 struct Client
*target_p
, const char *chname
, const char *op_flags
, struct who_format
*fmt
);
86 ** parv[0] = sender prefix
87 ** parv[1] = nickname mask list
88 ** parv[2] = additional selection flag and format options
91 m_who(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
93 static time_t last_used
= 0;
94 struct Client
*target_p
;
95 struct membership
*msptr
;
98 struct Channel
*chptr
= NULL
;
99 int server_oper
= parc
> 2 ? (*parv
[2] == 'o') : 0; /* Show OPERS only */
102 struct who_format fmt
;
106 fmt
.querytype
= NULL
;
107 if (parc
> 2 && (s
= strchr(parv
[2], '%')) != NULL
)
110 for (; *s
!= '\0'; s
++)
114 case 'c': fmt
.fields
|= FIELD_CHANNEL
; break;
115 case 'd': fmt
.fields
|= FIELD_HOP
; break;
116 case 'f': fmt
.fields
|= FIELD_FLAGS
; break;
117 case 'h': fmt
.fields
|= FIELD_HOST
; break;
118 case 'i': fmt
.fields
|= FIELD_IP
; break;
119 case 'l': fmt
.fields
|= FIELD_IDLE
; break;
120 case 'n': fmt
.fields
|= FIELD_NICK
; break;
121 case 'r': fmt
.fields
|= FIELD_INFO
; break;
122 case 's': fmt
.fields
|= FIELD_SERVER
; break;
123 case 't': fmt
.fields
|= FIELD_QUERYTYPE
; break;
124 case 'u': fmt
.fields
|= FIELD_USER
; break;
125 case 'a': fmt
.fields
|= FIELD_ACCOUNT
; break;
126 case 'o': fmt
.fields
|= FIELD_OPLEVEL
; break;
135 if (EmptyString(fmt
.querytype
) || strlen(fmt
.querytype
) > 3)
139 mask
= LOCAL_COPY(parv
[1]);
144 if((*(mask
+ 1) == '\0') && (*mask
== '*'))
146 if(source_p
->user
== NULL
)
149 if((lp
= source_p
->user
->channel
.head
) != NULL
)
152 do_who_on_channel(source_p
, msptr
->chptr
, server_oper
, YES
, &fmt
);
155 sendto_one(source_p
, form_str(RPL_ENDOFWHO
),
156 me
.name
, source_p
->name
, "*");
160 if(IsOperSpy(source_p
) && *mask
== '!')
165 if(EmptyString(mask
))
167 sendto_one(source_p
, form_str(RPL_ENDOFWHO
),
168 me
.name
, source_p
->name
, parv
[1]);
173 /* '/who #some_channel' */
174 if(IsChannelName(mask
))
176 /* List all users on a given channel */
177 chptr
= find_channel(parv
[1] + operspy
);
181 report_operspy(source_p
, "WHO", chptr
->chname
);
183 if(IsMember(source_p
, chptr
) || operspy
)
184 do_who_on_channel(source_p
, chptr
, server_oper
, YES
, &fmt
);
185 else if(!SecretChannel(chptr
))
186 do_who_on_channel(source_p
, chptr
, server_oper
, NO
, &fmt
);
188 sendto_one(source_p
, form_str(RPL_ENDOFWHO
),
189 me
.name
, source_p
->name
, parv
[1] + operspy
);
195 if(((target_p
= find_named_person(mask
)) != NULL
) &&
196 (!server_oper
|| IsOper(target_p
)))
200 isinvis
= IsInvisible(target_p
);
201 RB_DLINK_FOREACH(lp
, target_p
->user
->channel
.head
)
204 chptr
= msptr
->chptr
;
206 member
= IsMember(source_p
, chptr
);
208 if(isinvis
&& !member
)
211 if(member
|| (!isinvis
&& PubChannel(chptr
)))
215 /* if we stopped midlist, lp->data is the membership for
219 do_who(source_p
, target_p
, chptr
->chname
,
220 find_channel_status(lp
->data
, IsCapable(source_p
, CLICAP_MULTI_PREFIX
)), &fmt
);
222 do_who(source_p
, target_p
, NULL
, "", &fmt
);
224 sendto_one(source_p
, form_str(RPL_ENDOFWHO
),
225 me
.name
, source_p
->name
, mask
);
229 if(!IsFloodDone(source_p
))
230 flood_endgrace(source_p
);
232 /* it has to be a global who at this point, limit it */
233 if(!IsOper(source_p
))
235 if((last_used
+ ConfigFileEntry
.pace_wait
) > rb_current_time())
237 sendto_one(source_p
, form_str(RPL_LOAD2HI
),
238 me
.name
, source_p
->name
, "WHO");
239 sendto_one(source_p
, form_str(RPL_ENDOFWHO
),
240 me
.name
, source_p
->name
, "*");
244 last_used
= rb_current_time();
247 /* Note: operspy_dont_care_user_info does not apply to
249 if(IsOperSpy(source_p
) && ConfigFileEntry
.operspy_dont_care_user_info
)
252 /* '/who 0' for a global list. this forces clients to actually
253 * request a full list. I presume its because of too many typos
254 * with "/who" ;) --fl
256 if((*(mask
+ 1) == '\0') && (*mask
== '0'))
257 who_global(source_p
, NULL
, server_oper
, 0, &fmt
);
259 who_global(source_p
, mask
, server_oper
, operspy
, &fmt
);
261 sendto_one(source_p
, form_str(RPL_ENDOFWHO
),
262 me
.name
, source_p
->name
, mask
);
267 /* who_common_channel
268 * inputs - pointer to client requesting who
269 * - pointer to channel member chain.
270 * - char * mask to match
271 * - int if oper on a server or not
272 * - pointer to int maxmatches
275 * side effects - lists matching invisible clients on specified channel,
276 * marks matched clients.
279 who_common_channel(struct Client
*source_p
, struct Channel
*chptr
,
280 const char *mask
, int server_oper
, int *maxmatches
,
281 struct who_format
*fmt
)
283 struct membership
*msptr
;
284 struct Client
*target_p
;
287 RB_DLINK_FOREACH(ptr
, chptr
->members
.head
)
290 target_p
= msptr
->client_p
;
292 if(!IsInvisible(target_p
) || IsMarked(target_p
))
295 if(server_oper
&& !IsOper(target_p
))
303 match(mask
, target_p
->name
) || match(mask
, target_p
->username
) ||
304 match(mask
, target_p
->host
) || match(mask
, target_p
->servptr
->name
) ||
305 (IsOper(source_p
) && match(mask
, target_p
->orighost
)) ||
306 match(mask
, target_p
->info
))
308 do_who(source_p
, target_p
, NULL
, "", fmt
);
318 * inputs - pointer to client requesting who
319 * - char * mask to match
320 * - int if oper on a server or not
321 * - int if operspy or not
324 * side effects - do a global scan of all clients looking for match
325 * this is slightly expensive on EFnet ...
326 * marks assumed cleared for all clients initially
327 * and will be left cleared on return
330 who_global(struct Client
*source_p
, const char *mask
, int server_oper
, int operspy
, struct who_format
*fmt
)
332 struct membership
*msptr
;
333 struct Client
*target_p
;
334 rb_dlink_node
*lp
, *ptr
;
335 int maxmatches
= 500;
337 /* first, list all matching INvisible clients on common channels
338 * if this is not an operspy who
342 RB_DLINK_FOREACH(lp
, source_p
->user
->channel
.head
)
345 who_common_channel(source_p
, msptr
->chptr
, mask
, server_oper
, &maxmatches
, fmt
);
348 else if (!ConfigFileEntry
.operspy_dont_care_user_info
)
349 report_operspy(source_p
, "WHO", mask
);
351 /* second, list all matching visible clients and clear all marks
352 * on invisible clients
353 * if this is an operspy who, list all matching clients, no need
356 RB_DLINK_FOREACH(ptr
, global_client_list
.head
)
358 target_p
= ptr
->data
;
359 if(!IsPerson(target_p
))
362 if(IsInvisible(target_p
) && !operspy
)
368 if(server_oper
&& !IsOper(target_p
))
374 match(mask
, target_p
->name
) || match(mask
, target_p
->username
) ||
375 match(mask
, target_p
->host
) || match(mask
, target_p
->servptr
->name
) ||
376 (IsOper(source_p
) && match(mask
, target_p
->orighost
)) ||
377 match(mask
, target_p
->info
))
379 do_who(source_p
, target_p
, NULL
, "", fmt
);
387 form_str(ERR_TOOMANYMATCHES
),
388 me
.name
, source_p
->name
, "WHO");
394 * inputs - pointer to client requesting who
395 * - pointer to channel to do who on
396 * - The "real name" of this channel
397 * - int if source_p is a server oper or not
398 * - int if client is member or not
401 * side effects - do a who on given channel
404 do_who_on_channel(struct Client
*source_p
, struct Channel
*chptr
,
405 int server_oper
, int member
, struct who_format
*fmt
)
407 struct Client
*target_p
;
408 struct membership
*msptr
;
410 int combine
= IsCapable(source_p
, CLICAP_MULTI_PREFIX
);
412 RB_DLINK_FOREACH(ptr
, chptr
->members
.head
)
415 target_p
= msptr
->client_p
;
417 if(server_oper
&& !IsOper(target_p
))
420 if(member
|| !IsInvisible(target_p
))
421 do_who(source_p
, target_p
, chptr
->chname
,
422 find_channel_status(msptr
, combine
), fmt
);
429 * inputs - pointer to client requesting who
430 * - pointer to client to do who on
431 * - The reported name
435 * side effects - do a who on given person
439 do_who(struct Client
*source_p
, struct Client
*target_p
, const char *chname
, const char *op_flags
, struct who_format
*fmt
)
442 char str
[512], *p
, *end
;
445 rb_sprintf(status
, "%c%s%s",
446 target_p
->user
->away
? 'G' : 'H', IsOper(target_p
) ? "*" : "", op_flags
);
448 if (fmt
->fields
== 0)
449 sendto_one(source_p
, form_str(RPL_WHOREPLY
), me
.name
,
450 source_p
->name
, (chname
) ? (chname
) : "*",
451 target_p
->username
, target_p
->host
,
452 target_p
->servptr
->name
, target_p
->name
, status
,
453 ConfigServerHide
.flatten_links
? 0 : target_p
->hopcount
,
459 end
= str
+ sizeof str
;
460 if (fmt
->fields
& FIELD_QUERYTYPE
)
461 p
+= rb_snprintf(p
, end
- p
, " %s", fmt
->querytype
);
462 if (fmt
->fields
& FIELD_CHANNEL
)
463 p
+= rb_snprintf(p
, end
- p
, " %s", (chname
) ? (chname
) : "*");
464 if (fmt
->fields
& FIELD_USER
)
465 p
+= rb_snprintf(p
, end
- p
, " %s", target_p
->username
);
466 if (fmt
->fields
& FIELD_IP
)
468 if (show_ip(source_p
, target_p
) && !EmptyString(target_p
->sockhost
) && strcmp(target_p
->sockhost
, "0"))
469 p
+= rb_snprintf(p
, end
- p
, " %s", target_p
->sockhost
);
471 p
+= rb_snprintf(p
, end
- p
, " %s", "255.255.255.255");
473 if (fmt
->fields
& FIELD_HOST
)
474 p
+= rb_snprintf(p
, end
- p
, " %s", target_p
->host
);
475 if (fmt
->fields
& FIELD_SERVER
)
476 p
+= rb_snprintf(p
, end
- p
, " %s", target_p
->servptr
->name
);
477 if (fmt
->fields
& FIELD_NICK
)
478 p
+= rb_snprintf(p
, end
- p
, " %s", target_p
->name
);
479 if (fmt
->fields
& FIELD_FLAGS
)
480 p
+= rb_snprintf(p
, end
- p
, " %s", status
);
481 if (fmt
->fields
& FIELD_HOP
)
482 p
+= rb_snprintf(p
, end
- p
, " %d", ConfigServerHide
.flatten_links
? 0 : target_p
->hopcount
);
483 if (fmt
->fields
& FIELD_IDLE
)
484 p
+= rb_snprintf(p
, end
- p
, " %d", MyClient(target_p
) ? rb_current_time() - target_p
->localClient
->last
: 0);
485 if (fmt
->fields
& FIELD_ACCOUNT
)
487 /* display as in whois */
488 q
= target_p
->user
->suser
;
494 q
= target_p
->user
->suser
;
498 p
+= rb_snprintf(p
, end
- p
, " %s", q
);
500 if (fmt
->fields
& FIELD_OPLEVEL
)
501 p
+= rb_snprintf(p
, end
- p
, " %s", *op_flags
== '@' ? "999" : "n/a");
502 if (fmt
->fields
& FIELD_INFO
)
503 p
+= rb_snprintf(p
, end
- p
, " :%s", target_p
->info
);
504 sendto_one_numeric(source_p
, RPL_WHOSPCRPL
, "%s", str
+ 1);