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-2012 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
22  *  USA
23  *
24  *  $Id: m_who.c 27371 2012-03-16 05:33:15Z dubkat $
25  */
26 #include "stdinc.h"
27 #include "struct.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 "match.h"
36 #include "s_conf.h"
37 #include "s_log.h"
38 
39 #include "parse.h"
40 #include "modules.h"
41 #include "s_newconf.h"
42 
43 static int m_who(struct Client *, struct Client *, int, const char **);
44 
45 struct Message who_msgtab = {
46 	"WHO", 0, 0, 0, MFLG_SLOW,
47 	{mg_unreg, {m_who, 2}, mg_ignore, mg_ignore, mg_ignore, {m_who, 2}}
48 };
49 
50 mapi_clist_av1 who_clist[] = { &who_msgtab, NULL };
51 
52 DECLARE_MODULE_AV1(who, NULL, NULL, who_clist, NULL, NULL, "$Revision: 27371 $");
53 
54 static void do_who_on_channel(struct Client *source_p, struct Channel *chptr,
55 			      int server_oper, int member);
56 
57 static void who_global(struct Client *source_p, const char *mask, int server_oper, int operspy);
58 
59 static void do_who(struct Client *source_p,
60 		   struct Client *target_p, const char *chname, const char *op_flags);
61 
62 
63 /*
64 ** m_who
65 **      parv[0] = sender prefix
66 **      parv[1] = nickname mask list
67 **      parv[2] = additional selection flag, only 'o' for now.
68 */
69 static int
m_who(struct Client * client_p,struct Client * source_p,int parc,const char * parv[])70 m_who(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
71 {
72 	static time_t last_used = 0;
73 	struct Client *target_p;
74 	struct membership *msptr;
75 	char *mask;
76 	rb_dlink_node *lp;
77 	struct Channel *chptr = NULL;
78 	int server_oper = parc > 2 ? (*parv[2] == 'o') : 0;	/* Show OPERS only */
79 	int member;
80 	int operspy = 0;
81 
82 	mask = LOCAL_COPY(parv[1]);
83 
84 	collapse(mask);
85 
86 	/* '/who *' */
87 	if((*(mask + 1) == '\0') && (*mask == '*'))
88 	{
89 		if(source_p->user == NULL)
90 			return 0;
91 
92 		if((lp = source_p->user->channel.head) != NULL)
93 		{
94 			msptr = lp->data;
95 			SetCork(source_p);
96 			do_who_on_channel(source_p, msptr->chptr, server_oper, YES);
97 			ClearCork(source_p);
98 		}
99 
100 		sendto_one(source_p, form_str(RPL_ENDOFWHO), 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 */
121 		chptr = find_channel(parv[1] + operspy);
122 		if(chptr != NULL)
123 		{
124 			if(operspy)
125 				report_operspy(source_p, "WHO", chptr->chname);
126 			SetCork(source_p);
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 			ClearCork(source_p);
132 		}
133 		sendto_one(source_p, form_str(RPL_ENDOFWHO),
134 			   me.name, source_p->name, parv[1] + operspy);
135 		return 0;
136 	}
137 
138 	/* '/who nick' */
139 
140 	if(((target_p = find_named_person(mask)) != NULL) && (!server_oper || IsOper(target_p)))
141 	{
142 		int isinvis = 0;
143 
144 		isinvis = IsInvisible(target_p);
145 		RB_DLINK_FOREACH(lp, target_p->user->channel.head)
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,
165 						   IsCapable(source_p, CLICAP_MULTI_PREFIX)));
166 		else
167 			do_who(source_p, target_p, NULL, "");
168 
169 		sendto_one(source_p, form_str(RPL_ENDOFWHO), 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 	{
179 		if((last_used + ConfigFileEntry.pace_wait) > rb_current_time())
180 		{
181 			sendto_one(source_p, form_str(RPL_LOAD2HI), me.name, source_p->name, "WHO");
182 			sendto_one(source_p, form_str(RPL_ENDOFWHO), me.name, source_p->name, "*");
183 			return 0;
184 		}
185 		else
186 			last_used = rb_current_time();
187 	}
188 
189 	/* '/who 0' for a global list.  this forces clients to actually
190 	 * request a full list.  I presume its because of too many typos
191 	 * with "/who" ;) --fl
192 	 */
193 	SetCork(source_p);
194 	if((*(mask + 1) == '\0') && (*mask == '0'))
195 		who_global(source_p, NULL, server_oper, 0);
196 	else
197 		who_global(source_p, mask, server_oper, operspy);
198 	ClearCork(source_p);
199 	sendto_one(source_p, form_str(RPL_ENDOFWHO), me.name, source_p->name, mask);
200 
201 	return 0;
202 }
203 
204 /* who_common_channel
205  * inputs	- pointer to client requesting who
206  * 		- pointer to channel member chain.
207  *		- char * mask to match
208  *		- int if oper on a server or not
209  *		- pointer to int maxmatches
210  * output	- NONE
211  * side effects - lists matching invisible clients on specified channel,
212  * 		  marks matched clients.
213  */
214 static void
who_common_channel(struct Client * source_p,struct Channel * chptr,const char * mask,int server_oper,int * maxmatches)215 who_common_channel(struct Client *source_p, struct Channel *chptr,
216 		   const char *mask, int server_oper, int *maxmatches)
217 {
218 	struct membership *msptr;
219 	struct Client *target_p;
220 	rb_dlink_node *ptr;
221 
222 	RB_DLINK_FOREACH(ptr, chptr->members.head)
223 	{
224 		msptr = ptr->data;
225 		target_p = msptr->client_p;
226 
227 		if(!IsInvisible(target_p) || IsMarked(target_p))
228 			continue;
229 
230 		if(server_oper && !IsOper(target_p))
231 			continue;
232 
233 		SetMark(target_p);
234 
235 		if(*maxmatches > 0)
236 		{
237 			if((mask == NULL) ||
238 			   match(mask, target_p->name) || match(mask, target_p->username) ||
239 			   match(mask, target_p->host) || match(mask, target_p->servptr->name) ||
240 			   match(mask, target_p->info))
241 			{
242 				do_who(source_p, target_p, NULL, "");
243 				--(*maxmatches);
244 			}
245 		}
246 	}
247 }
248 
249 /*
250  * who_global
251  *
252  * inputs	- pointer to client requesting who
253  *		- char * mask to match
254  *		- int if oper on a server or not
255  * output	- NONE
256  * side effects - do a global scan of all clients looking for match
257  *		  this is slightly expensive on EFnet ...
258  *		  marks assumed cleared for all clients initially
259  *		  and will be left cleared on return
260  */
261 static void
who_global(struct Client * source_p,const char * mask,int server_oper,int operspy)262 who_global(struct Client *source_p, const char *mask, int server_oper, int operspy)
263 {
264 	struct membership *msptr;
265 	struct Client *target_p;
266 	rb_dlink_node *lp, *ptr;
267 	int maxmatches = 500;
268 
269 	/* first, list all matching INvisible clients on common channels
270 	 * if this is not an operspy who
271 	 */
272 	if(!operspy)
273 	{
274 		RB_DLINK_FOREACH(lp, source_p->user->channel.head)
275 		{
276 			msptr = lp->data;
277 			who_common_channel(source_p, msptr->chptr, mask, server_oper, &maxmatches);
278 		}
279 	}
280 	else
281 		report_operspy(source_p, "WHO", mask);
282 
283 	/* second, list all matching visible clients and clear all marks
284 	 * on invisible clients
285 	 * if this is an operspy who, list all matching clients, no need
286 	 * to clear marks
287 	 */
288 	RB_DLINK_FOREACH(ptr, global_client_list.head)
289 	{
290 		target_p = ptr->data;
291 		if(!IsClient(target_p))
292 			continue;
293 
294 		if(IsInvisible(target_p) && !operspy)
295 		{
296 			ClearMark(target_p);
297 			continue;
298 		}
299 
300 		if(server_oper && !IsOper(target_p))
301 			continue;
302 
303 		if(maxmatches > 0)
304 		{
305 			if(!mask ||
306 			   match(mask, target_p->name) || match(mask, target_p->username) ||
307 			   match(mask, target_p->host) || match(mask, target_p->servptr->name) ||
308 			   match(mask, target_p->info))
309 			{
310 				do_who(source_p, target_p, NULL, "");
311 				--maxmatches;
312 			}
313 		}
314 
315 	}
316 
317 	if(maxmatches <= 0)
318 		sendto_one(source_p, form_str(ERR_TOOMANYMATCHES), me.name, source_p->name, "WHO");
319 }
320 
321 /*
322  * do_who_on_channel
323  *
324  * inputs	- pointer to client requesting who
325  *		- pointer to channel to do who on
326  *		- The "real name" of this channel
327  *		- int if source_p is a server oper or not
328  *		- int if client is member or not
329  * output	- NONE
330  * side effects - do a who on given channel
331  */
332 static void
do_who_on_channel(struct Client * source_p,struct Channel * chptr,int server_oper,int member)333 do_who_on_channel(struct Client *source_p, struct Channel *chptr, int server_oper, int member)
334 {
335 	struct Client *target_p;
336 	struct membership *msptr;
337 	rb_dlink_node *ptr;
338 	int combine = IsCapable(source_p, CLICAP_MULTI_PREFIX);
339 
340 	RB_DLINK_FOREACH(ptr, chptr->members.head)
341 	{
342 		msptr = ptr->data;
343 		target_p = msptr->client_p;
344 
345 		if(server_oper && !IsOper(target_p))
346 			continue;
347 
348 		if(member || !IsInvisible(target_p))
349 			do_who(source_p, target_p, chptr->chname,
350 			       find_channel_status(msptr, combine));
351 	}
352 }
353 
354 /*
355  * do_who
356  *
357  * inputs	- pointer to client requesting who
358  *		- pointer to client to do who on
359  *		- The reported name
360  *		- channel flags
361  * output	- NONE
362  * side effects - do a who on given person
363  */
364 
365 static void
do_who(struct Client * source_p,struct Client * target_p,const char * chname,const char * op_flags)366 do_who(struct Client *source_p, struct Client *target_p, const char *chname, const char *op_flags)
367 {
368 	char status[5];
369 
370 	rb_snprintf(status, sizeof(status), "%c%s%s",
371 		    target_p->user->away ? 'G' : 'H', IsOper(target_p) ? "*" : "", op_flags);
372 
373 	sendto_one(source_p, form_str(RPL_WHOREPLY), me.name, source_p->name,
374 		   (chname) ? (chname) : "*",
375 		   target_p->username,
376 		   target_p->host, target_p->servptr->name, target_p->name,
377 		   status, ConfigServerHide.flatten_links ? 0 : target_p->hopcount, target_p->info);
378 }
379