]>
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 | |
23 | * | |
f71e18ee | 24 | * $Id: m_who.c 3350 2007-04-02 22:03:08Z jilles $ |
212380e3 AC |
25 | */ |
26 | #include "stdinc.h" | |
212380e3 AC |
27 | #include "common.h" |
28 | #include "client.h" | |
29 | #include "channel.h" | |
30 | #include "hash.h" | |
31 | #include "ircd.h" | |
32 | #include "numeric.h" | |
33 | #include "s_serv.h" | |
34 | #include "send.h" | |
35 | #include "irc_string.h" | |
36 | #include "sprintf_irc.h" | |
37 | #include "s_conf.h" | |
4016731b | 38 | #include "logger.h" |
212380e3 AC |
39 | #include "msg.h" |
40 | #include "parse.h" | |
41 | #include "modules.h" | |
42 | #include "packet.h" | |
43 | #include "s_newconf.h" | |
44 | ||
45 | static int m_who(struct Client *, struct Client *, int, const char **); | |
46 | ||
47 | struct Message who_msgtab = { | |
48 | "WHO", 0, 0, 0, MFLG_SLOW, | |
49 | {mg_unreg, {m_who, 2}, mg_ignore, mg_ignore, mg_ignore, {m_who, 2}} | |
50 | }; | |
51 | ||
52 | mapi_clist_av1 who_clist[] = { &who_msgtab, NULL }; | |
f71e18ee | 53 | DECLARE_MODULE_AV1(who, NULL, NULL, who_clist, NULL, NULL, "$Revision: 3350 $"); |
212380e3 AC |
54 | |
55 | static void do_who_on_channel(struct Client *source_p, struct Channel *chptr, | |
56 | int server_oper, int member); | |
57 | ||
58 | static void who_global(struct Client *source_p, const char *mask, int server_oper, int operspy); | |
59 | ||
60 | static void do_who(struct Client *source_p, | |
61 | struct Client *target_p, const char *chname, const char *op_flags); | |
62 | ||
63 | ||
64 | /* | |
65 | ** m_who | |
66 | ** parv[0] = sender prefix | |
67 | ** parv[1] = nickname mask list | |
68 | ** parv[2] = additional selection flag, only 'o' for now. | |
69 | */ | |
70 | static int | |
71 | m_who(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) | |
72 | { | |
73 | static time_t last_used = 0; | |
74 | struct Client *target_p; | |
75 | struct membership *msptr; | |
76 | char *mask; | |
5b96d9a6 | 77 | rb_dlink_node *lp; |
212380e3 AC |
78 | struct Channel *chptr = NULL; |
79 | int server_oper = parc > 2 ? (*parv[2] == 'o') : 0; /* Show OPERS only */ | |
80 | int member; | |
81 | int operspy = 0; | |
82 | ||
83 | mask = LOCAL_COPY(parv[1]); | |
84 | ||
85 | collapse(mask); | |
86 | ||
87 | /* '/who *' */ | |
88 | if((*(mask + 1) == '\0') && (*mask == '*')) | |
89 | { | |
90 | if(source_p->user == NULL) | |
91 | return 0; | |
92 | ||
93 | if((lp = source_p->user->channel.head) != NULL) | |
94 | { | |
95 | msptr = lp->data; | |
96 | do_who_on_channel(source_p, msptr->chptr, server_oper, YES); | |
97 | } | |
98 | ||
99 | sendto_one(source_p, form_str(RPL_ENDOFWHO), | |
100 | me.name, source_p->name, "*"); | |
101 | return 0; | |
102 | } | |
103 | ||
104 | if(IsOperSpy(source_p) && *mask == '!') | |
105 | { | |
106 | mask++; | |
107 | operspy = 1; | |
108 | ||
109 | if(EmptyString(mask)) | |
110 | { | |
111 | sendto_one(source_p, form_str(RPL_ENDOFWHO), | |
112 | me.name, source_p->name, parv[1]); | |
113 | return 0; | |
114 | } | |
115 | } | |
116 | ||
117 | /* '/who #some_channel' */ | |
118 | if(IsChannelName(mask)) | |
119 | { | |
120 | /* List all users on a given channel */ | |
f71e18ee | 121 | chptr = find_channel(parv[1] + operspy); |
212380e3 AC |
122 | if(chptr != NULL) |
123 | { | |
124 | if(operspy) | |
125 | report_operspy(source_p, "WHO", chptr->chname); | |
126 | ||
127 | if(IsMember(source_p, chptr) || operspy) | |
128 | do_who_on_channel(source_p, chptr, server_oper, YES); | |
129 | else if(!SecretChannel(chptr)) | |
130 | do_who_on_channel(source_p, chptr, server_oper, NO); | |
131 | } | |
132 | sendto_one(source_p, form_str(RPL_ENDOFWHO), | |
f71e18ee | 133 | me.name, source_p->name, parv[1] + operspy); |
212380e3 AC |
134 | return 0; |
135 | } | |
136 | ||
137 | /* '/who nick' */ | |
138 | ||
139 | if(((target_p = find_named_person(mask)) != NULL) && | |
140 | (!server_oper || IsOper(target_p))) | |
141 | { | |
142 | int isinvis = 0; | |
143 | ||
144 | isinvis = IsInvisible(target_p); | |
5b96d9a6 | 145 | RB_DLINK_FOREACH(lp, target_p->user->channel.head) |
212380e3 AC |
146 | { |
147 | msptr = lp->data; | |
148 | chptr = msptr->chptr; | |
149 | ||
150 | member = IsMember(source_p, chptr); | |
151 | ||
152 | if(isinvis && !member) | |
153 | continue; | |
154 | ||
155 | if(member || (!isinvis && PubChannel(chptr))) | |
156 | break; | |
157 | } | |
158 | ||
159 | /* if we stopped midlist, lp->data is the membership for | |
160 | * target_p of chptr | |
161 | */ | |
162 | if(lp != NULL) | |
163 | do_who(source_p, target_p, chptr->chname, | |
164 | find_channel_status(lp->data, IsCapable(source_p, CLICAP_MULTI_PREFIX))); | |
165 | else | |
166 | do_who(source_p, target_p, NULL, ""); | |
167 | ||
168 | sendto_one(source_p, form_str(RPL_ENDOFWHO), | |
169 | me.name, source_p->name, mask); | |
170 | return 0; | |
171 | } | |
172 | ||
173 | if(!IsFloodDone(source_p)) | |
174 | flood_endgrace(source_p); | |
175 | ||
176 | /* it has to be a global who at this point, limit it */ | |
177 | if(!IsOper(source_p)) | |
178 | { | |
e3354945 | 179 | if((last_used + ConfigFileEntry.pace_wait) > rb_current_time()) |
212380e3 AC |
180 | { |
181 | sendto_one(source_p, form_str(RPL_LOAD2HI), | |
182 | me.name, source_p->name, "WHO"); | |
183 | sendto_one(source_p, form_str(RPL_ENDOFWHO), | |
184 | me.name, source_p->name, "*"); | |
185 | return 0; | |
186 | } | |
187 | else | |
e3354945 | 188 | last_used = rb_current_time(); |
212380e3 AC |
189 | } |
190 | ||
191 | /* Note: operspy_dont_care_user_info does not apply to | |
192 | * who on channels */ | |
193 | if(IsOperSpy(source_p) && ConfigFileEntry.operspy_dont_care_user_info) | |
194 | operspy = 1; | |
195 | ||
196 | /* '/who 0' for a global list. this forces clients to actually | |
197 | * request a full list. I presume its because of too many typos | |
198 | * with "/who" ;) --fl | |
199 | */ | |
200 | if((*(mask + 1) == '\0') && (*mask == '0')) | |
201 | who_global(source_p, NULL, server_oper, 0); | |
202 | else | |
203 | who_global(source_p, mask, server_oper, operspy); | |
204 | ||
205 | sendto_one(source_p, form_str(RPL_ENDOFWHO), | |
206 | me.name, source_p->name, mask); | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | /* who_common_channel | |
212 | * inputs - pointer to client requesting who | |
213 | * - pointer to channel member chain. | |
214 | * - char * mask to match | |
215 | * - int if oper on a server or not | |
216 | * - pointer to int maxmatches | |
217 | * output - NONE | |
218 | * side effects - lists matching invisible clients on specified channel, | |
219 | * marks matched clients. | |
220 | */ | |
221 | static void | |
222 | who_common_channel(struct Client *source_p, struct Channel *chptr, | |
223 | const char *mask, int server_oper, int *maxmatches) | |
224 | { | |
225 | struct membership *msptr; | |
226 | struct Client *target_p; | |
5b96d9a6 | 227 | rb_dlink_node *ptr; |
212380e3 | 228 | |
5b96d9a6 | 229 | RB_DLINK_FOREACH(ptr, chptr->members.head) |
212380e3 AC |
230 | { |
231 | msptr = ptr->data; | |
232 | target_p = msptr->client_p; | |
233 | ||
234 | if(!IsInvisible(target_p) || IsMarked(target_p)) | |
235 | continue; | |
236 | ||
237 | if(server_oper && !IsOper(target_p)) | |
238 | continue; | |
239 | ||
240 | SetMark(target_p); | |
241 | ||
242 | if(*maxmatches > 0) | |
243 | { | |
244 | if((mask == NULL) || | |
245 | match(mask, target_p->name) || match(mask, target_p->username) || | |
c88cdb00 | 246 | match(mask, target_p->host) || match(mask, target_p->servptr->name) || |
212380e3 AC |
247 | (IsOper(source_p) && match(mask, target_p->orighost)) || |
248 | match(mask, target_p->info)) | |
249 | { | |
250 | do_who(source_p, target_p, NULL, ""); | |
251 | --(*maxmatches); | |
252 | } | |
253 | } | |
254 | } | |
255 | } | |
256 | ||
257 | /* | |
258 | * who_global | |
259 | * | |
260 | * inputs - pointer to client requesting who | |
261 | * - char * mask to match | |
262 | * - int if oper on a server or not | |
263 | * output - NONE | |
264 | * side effects - do a global scan of all clients looking for match | |
265 | * this is slightly expensive on EFnet ... | |
266 | * marks assumed cleared for all clients initially | |
267 | * and will be left cleared on return | |
268 | */ | |
269 | static void | |
270 | who_global(struct Client *source_p, const char *mask, int server_oper, int operspy) | |
271 | { | |
272 | struct membership *msptr; | |
273 | struct Client *target_p; | |
5b96d9a6 | 274 | rb_dlink_node *lp, *ptr; |
212380e3 AC |
275 | int maxmatches = 500; |
276 | ||
277 | /* first, list all matching INvisible clients on common channels | |
278 | * if this is not an operspy who | |
279 | */ | |
280 | if(!operspy) | |
281 | { | |
5b96d9a6 | 282 | RB_DLINK_FOREACH(lp, source_p->user->channel.head) |
212380e3 AC |
283 | { |
284 | msptr = lp->data; | |
285 | who_common_channel(source_p, msptr->chptr, mask, server_oper, &maxmatches); | |
286 | } | |
287 | } | |
288 | else if (!ConfigFileEntry.operspy_dont_care_user_info) | |
289 | report_operspy(source_p, "WHO", mask); | |
290 | ||
291 | /* second, list all matching visible clients and clear all marks | |
292 | * on invisible clients | |
293 | * if this is an operspy who, list all matching clients, no need | |
294 | * to clear marks | |
295 | */ | |
5b96d9a6 | 296 | RB_DLINK_FOREACH(ptr, global_client_list.head) |
212380e3 AC |
297 | { |
298 | target_p = ptr->data; | |
299 | if(!IsPerson(target_p)) | |
300 | continue; | |
301 | ||
302 | if(IsInvisible(target_p) && !operspy) | |
303 | { | |
304 | ClearMark(target_p); | |
305 | continue; | |
306 | } | |
307 | ||
308 | if(server_oper && !IsOper(target_p)) | |
309 | continue; | |
310 | ||
311 | if(maxmatches > 0) | |
312 | { | |
313 | if(!mask || | |
314 | match(mask, target_p->name) || match(mask, target_p->username) || | |
c88cdb00 | 315 | match(mask, target_p->host) || match(mask, target_p->servptr->name) || |
212380e3 AC |
316 | (IsOper(source_p) && match(mask, target_p->orighost)) || |
317 | match(mask, target_p->info)) | |
318 | { | |
319 | do_who(source_p, target_p, NULL, ""); | |
320 | --maxmatches; | |
321 | } | |
322 | } | |
323 | } | |
324 | ||
325 | if (maxmatches <= 0) | |
326 | sendto_one(source_p, | |
327 | form_str(ERR_TOOMANYMATCHES), | |
328 | me.name, source_p->name, "WHO"); | |
329 | } | |
330 | ||
331 | /* | |
332 | * do_who_on_channel | |
333 | * | |
334 | * inputs - pointer to client requesting who | |
335 | * - pointer to channel to do who on | |
336 | * - The "real name" of this channel | |
337 | * - int if source_p is a server oper or not | |
338 | * - int if client is member or not | |
339 | * output - NONE | |
340 | * side effects - do a who on given channel | |
341 | */ | |
342 | static void | |
343 | do_who_on_channel(struct Client *source_p, struct Channel *chptr, | |
344 | int server_oper, int member) | |
345 | { | |
346 | struct Client *target_p; | |
347 | struct membership *msptr; | |
5b96d9a6 | 348 | rb_dlink_node *ptr; |
212380e3 AC |
349 | int combine = IsCapable(source_p, CLICAP_MULTI_PREFIX); |
350 | ||
5b96d9a6 | 351 | RB_DLINK_FOREACH(ptr, chptr->members.head) |
212380e3 AC |
352 | { |
353 | msptr = ptr->data; | |
354 | target_p = msptr->client_p; | |
355 | ||
356 | if(server_oper && !IsOper(target_p)) | |
357 | continue; | |
358 | ||
359 | if(member || !IsInvisible(target_p)) | |
360 | do_who(source_p, target_p, chptr->chname, | |
361 | find_channel_status(msptr, combine)); | |
362 | } | |
363 | } | |
364 | ||
365 | /* | |
366 | * do_who | |
367 | * | |
368 | * inputs - pointer to client requesting who | |
369 | * - pointer to client to do who on | |
370 | * - The reported name | |
371 | * - channel flags | |
372 | * output - NONE | |
373 | * side effects - do a who on given person | |
374 | */ | |
375 | ||
376 | static void | |
377 | do_who(struct Client *source_p, struct Client *target_p, const char *chname, const char *op_flags) | |
378 | { | |
379 | char status[5]; | |
380 | ||
7cdb0a09 | 381 | rb_sprintf(status, "%c%s%s", |
212380e3 AC |
382 | target_p->user->away ? 'G' : 'H', IsOper(target_p) ? "*" : "", op_flags); |
383 | ||
384 | sendto_one(source_p, form_str(RPL_WHOREPLY), me.name, source_p->name, | |
385 | (chname) ? (chname) : "*", | |
386 | target_p->username, | |
c88cdb00 | 387 | target_p->host, target_p->servptr->name, target_p->name, |
212380e3 AC |
388 | status, |
389 | ConfigServerHide.flatten_links ? 0 : target_p->hopcount, | |
390 | target_p->info); | |
391 | } |