]>
jfr.im git - irc/quakenet/snircd.git/blob - ircd/m_who.c
2 * IRC - Internet Relay Chat, ircd/m_who.c
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Computing Center
6 * See file AUTHORS in IRC package for additional names of
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 1, or (at your option)
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., 675 Mass Ave, Cambridge, MA 02139, USA.
23 * $Id: m_who.c,v 1.22.2.1 2005/10/01 03:45:19 entrope Exp $
27 * m_functions execute protocol messages on this server:
29 * cptr is always NON-NULL, pointing to a *LOCAL* client
30 * structure (with an open socket connected!). This
31 * identifies the physical socket where the message
32 * originated (or which caused the m_function to be
33 * executed--some m_functions may call others...).
35 * sptr is the source of the message, defined by the
36 * prefix part of the message if present. If not
37 * or prefix not found, then sptr==cptr.
39 * (!IsServer(cptr)) => (cptr == sptr), because
40 * prefixes are taken *only* from servers...
43 * (sptr == cptr) => the message didn't
46 * (sptr != cptr && IsServer(sptr) means
47 * the prefix specified servername. (?)
49 * (sptr != cptr && !IsServer(sptr) means
50 * that message originated from a remote
55 * (!IsServer(sptr)) means that, sptr can safely
56 * taken as defining the target structure of the
57 * message in this server.
59 * *Always* true (if 'parse' and others are working correct):
61 * 1) sptr->from == cptr (note: cptr->from == cptr)
63 * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
64 * *cannot* be a local connection, unless it's
65 * actually cptr!). [MyConnect(x) should probably
66 * be defined as (x == x->from) --msa ]
68 * parc number of variable parameter strings (if zero,
69 * parv is allowed to be NULL)
71 * parv a NULL terminated list of parameter pointers,
73 * parv[0], sender (prefix string), if not present
74 * this points to an empty string.
75 * parv[1]...parv[parc-1]
76 * pointers to additional parameters
77 * parv[parc] == NULL, *always*
79 * note: it is guaranteed that parv[0]..parv[parc-1] are all
88 #include "ircd_chattr.h"
89 #include "ircd_features.h"
91 #include "ircd_reply.h"
92 #include "ircd_string.h"
99 /* #include <assert.h> -- Now using assert in ircd_log.h */
104 * A little spin-marking utility to tell us which clients we have already
105 * processed and which not
107 static int who_marker
= 0;
108 static void move_marker(void)
112 struct Client
*cptr
= GlobalClientList
;
115 cli_marker(cptr
) = 0;
116 cptr
= cli_next(cptr
);
122 #define CheckMark(x, y) ((x == y) ? 0 : (x = y))
123 #define Process(cptr) CheckMark(cli_marker(cptr), who_marker)
126 * m_who - generic message handler
128 * parv[0] = sender prefix
129 * parv[1] = nickname mask list
130 * parv[2] = additional selection flag, only 'o' for now.
131 * and %flags to specify what fields to output
132 * plus a ,querytype if the t flag is specified
133 * so the final thing will be like o%tnchu,777
134 * parv[3] = _optional_ parameter that overrides parv[1]
135 * This can be used as "/quote who foo % :The Black Hacker
136 * to find me, parv[3] _can_ contain spaces !.
138 int m_who(struct Client
* cptr
, struct Client
* sptr
, int parc
, char* parv
[])
140 char *mask
; /* The mask we are looking for */
141 char ch
; /* Scratch char register */
142 struct Channel
*chptr
; /* Channel to show */
143 struct Client
*acptr
; /* Client to show */
145 int bitsel
; /* Mask of selectors to apply */
146 int matchsel
; /* Which fields the match should apply on */
147 int counter
; /* Query size counter,
148 initially used to count fields */
149 int commas
; /* Does our mask contain any comma ?
151 int fields
; /* Mask of fields to show */
152 int isthere
= 0; /* When this set the user is member of chptr */
153 char *nick
; /* Single element extracted from
155 char *p
; /* Scratch char pointer */
156 char *qrt
; /* Pointer to the query type */
157 static char mymask
[512]; /* To save the mask before corrupting it */
159 /* Let's find where is our mask, and if actually contains something */
160 mask
= ((parc
> 1) ? parv
[1] : 0);
161 if (parc
> 3 && parv
[3])
163 if (mask
&& ((mask
[0] == '\0') ||
164 (mask
[1] == '\0' && ((mask
[0] == '0') || (mask
[0] == '*')))))
167 /* Evaluate the flags now, we consider the second parameter
168 as "matchFlags%fieldsToInclude,querytype" */
169 bitsel
= fields
= counter
= matchsel
= 0;
171 if (parc
> 2 && parv
[2] && *parv
[2])
174 while (((ch
= *(p
++))) && (ch
!= '%') && (ch
!= ','))
179 bitsel
|= WHOSELECT_DELAY
;
183 bitsel
|= WHOSELECT_OPER
;
187 bitsel
|= WHOSELECT_EXTRA
;
188 if (HasPriv(sptr
, PRIV_WHOX
))
189 log_write(LS_WHO
, L_INFO
, LOG_NOSNOTICE
, "%#C WHO %s %s", sptr
,
190 (BadPtr(parv
[3]) ? parv
[1] : parv
[3]), parv
[2]);
194 matchsel
|= WHO_FIELD_NIC
;
198 matchsel
|= WHO_FIELD_UID
;
202 matchsel
|= WHO_FIELD_HOS
;
206 matchsel
|= WHO_FIELD_NIP
;
210 matchsel
|= WHO_FIELD_SER
;
214 matchsel
|= WHO_FIELD_REN
;
218 matchsel
|= WHO_FIELD_ACC
;
222 while ((ch
= *p
++) && (ch
!= ','))
229 fields
|= WHO_FIELD_CHA
;
233 fields
|= WHO_FIELD_DIS
;
237 fields
|= WHO_FIELD_FLA
;
241 fields
|= WHO_FIELD_HOS
;
245 fields
|= WHO_FIELD_NIP
;
249 fields
|= WHO_FIELD_IDL
;
252 fields
|= WHO_FIELD_NIC
;
256 fields
|= WHO_FIELD_REN
;
260 fields
|= WHO_FIELD_SER
;
264 fields
|= WHO_FIELD_QTY
;
268 fields
|= WHO_FIELD_UID
;
272 fields
|= WHO_FIELD_ACC
;
283 matchsel
= WHO_FIELD_DEF
;
287 if (feature_bool(FEAT_HIS_WHO_SERVERNAME
) && !IsAnOper(sptr
))
288 matchsel
&= ~WHO_FIELD_SER
;
290 if (feature_bool(FEAT_HIS_WHO_FILTERIP
) && !IsAnOper(sptr
))
291 matchsel
&= ~WHO_FIELD_NIP
;
293 if (qrt
&& (fields
& WHO_FIELD_QTY
))
296 if (!((*p
> '9') || (*p
< '0')))
298 if (!((*p
> '9') || (*p
< '0')))
300 if (!((*p
> '9') || (*p
< '0')))
307 /* I'd love to add also a check on the number of matches fields per time */
308 counter
= (2048 / (counter
+ 4));
309 if (mask
&& (strlen(mask
) > 510))
312 commas
= (mask
&& strchr(mask
, ','));
314 /* First treat mask as a list of plain nicks/channels */
317 strcpy(mymask
, mask
);
318 for (p
= 0, nick
= ircd_strtok(&p
, mymask
, ","); nick
;
319 nick
= ircd_strtok(&p
, 0, ","))
321 if (IsChannelName(nick
) && (chptr
= FindChannel(nick
)))
323 isthere
= (find_channel_member(sptr
, chptr
) != 0);
324 if (isthere
|| SEE_CHANNEL(sptr
, chptr
, bitsel
))
326 struct Membership
* member
;
327 for (member
= chptr
->members
; member
; member
= member
->next_member
)
329 acptr
= member
->user
;
330 if ((bitsel
& WHOSELECT_OPER
) && !SeeOper(sptr
,acptr
))
333 && ((member
->status
& CHFL_ZOMBIE
)
334 || ((member
->status
& CHFL_DELAYED
)
335 && !(bitsel
& WHOSELECT_DELAY
))))
337 if (!(isthere
|| (SEE_USER(sptr
, acptr
, bitsel
))))
339 if (!Process(acptr
)) /* This can't be moved before other checks */
341 if (!(isthere
|| (SHOW_MORE(sptr
, counter
))))
343 do_who(sptr
, acptr
, chptr
, fields
, qrt
);
349 if ((acptr
= FindUser(nick
)) &&
350 ((!(bitsel
& WHOSELECT_OPER
)) || SeeOper(sptr
,acptr
)) &&
351 Process(acptr
) && SHOW_MORE(sptr
, counter
))
353 do_who(sptr
, acptr
, 0, fields
, qrt
);
359 /* If we didn't have any comma in the mask treat it as a
360 real mask and try to match all relevant fields */
361 if (!(commas
|| (counter
< 1)))
363 struct irc_in_addr imask
;
369 matchcomp(mymask
, &minlen
, &cset
, mask
);
370 if (!ipmask_parse(mask
, &imask
, &ibits
))
371 matchsel
&= ~WHO_FIELD_NIP
;
372 if ((minlen
> NICKLEN
) || !(cset
& NTL_IRCNK
))
373 matchsel
&= ~WHO_FIELD_NIC
;
374 if ((matchsel
& WHO_FIELD_SER
) &&
375 ((minlen
> HOSTLEN
) || (!(cset
& NTL_IRCHN
))
376 || (!markMatchexServer(mymask
, minlen
))))
377 matchsel
&= ~WHO_FIELD_SER
;
378 if ((minlen
> USERLEN
) || !(cset
& NTL_IRCUI
))
379 matchsel
&= ~WHO_FIELD_UID
;
380 if ((minlen
> HOSTLEN
) || !(cset
& NTL_IRCHN
))
381 matchsel
&= ~WHO_FIELD_HOS
;
382 if ((minlen
> ACCOUNTLEN
))
383 matchsel
&= ~WHO_FIELD_ACC
;
386 /* First of all loop through the clients in common channels */
387 if ((!(counter
< 1)) && matchsel
) {
388 struct Membership
* member
;
389 struct Membership
* chan
;
390 for (chan
= cli_user(sptr
)->channel
; chan
; chan
= chan
->next_channel
) {
391 chptr
= chan
->channel
;
392 for (member
= chptr
->members
; member
; member
= member
->next_member
)
394 acptr
= member
->user
;
395 if (!(IsUser(acptr
) && Process(acptr
)))
396 continue; /* Now Process() is at the beginning, if we fail
397 we'll never have to show this acptr in this query */
398 if ((bitsel
& WHOSELECT_OPER
) && !SeeOper(sptr
,acptr
))
401 ((!(matchsel
& WHO_FIELD_NIC
))
402 || matchexec(cli_name(acptr
), mymask
, minlen
))
403 && ((!(matchsel
& WHO_FIELD_UID
))
404 || matchexec(cli_user(acptr
)->username
, mymask
, minlen
))
405 && ((!(matchsel
& WHO_FIELD_SER
))
406 || (!(HasFlag(cli_user(acptr
)->server
, FLAG_MAP
))))
407 && ((!(matchsel
& WHO_FIELD_HOS
))
408 || matchexec(cli_user(acptr
)->host
, mymask
, minlen
))
409 && ((!(matchsel
& WHO_FIELD_HOS
))
410 || !HasSetHost(acptr
)
411 || !HasHiddenHost(acptr
)
413 || matchexec(cli_user(acptr
)->realhost
, mymask
, minlen
))
414 && ((!(matchsel
& WHO_FIELD_REN
))
415 || matchexec(cli_info(acptr
), mymask
, minlen
))
416 && ((!(matchsel
& WHO_FIELD_NIP
))
417 || ((HasHiddenHost(acptr
) || HasSetHost(acptr
)) && !IsAnOper(sptr
))
418 || !ipmask_check(&cli_ip(acptr
), &imask
, ibits
))
419 && ((!(matchsel
& WHO_FIELD_ACC
))
420 || matchexec(cli_user(acptr
)->account
, mymask
, minlen
)))
422 if (!SHOW_MORE(sptr
, counter
))
424 do_who(sptr
, acptr
, chptr
, fields
, qrt
);
428 /* Loop through all clients :-\, if we still have something to match to
429 and we can show more clients */
430 if ((!(counter
< 1)) && matchsel
)
431 for (acptr
= cli_prev(&me
); acptr
; acptr
= cli_prev(acptr
))
433 if (!(IsUser(acptr
) && Process(acptr
)))
435 if ((bitsel
& WHOSELECT_OPER
) && !SeeOper(sptr
,acptr
))
437 if (!(SEE_USER(sptr
, acptr
, bitsel
)))
440 ((!(matchsel
& WHO_FIELD_NIC
))
441 || matchexec(cli_name(acptr
), mymask
, minlen
))
442 && ((!(matchsel
& WHO_FIELD_UID
))
443 || matchexec(cli_user(acptr
)->username
, mymask
, minlen
))
444 && ((!(matchsel
& WHO_FIELD_SER
))
445 || (!(HasFlag(cli_user(acptr
)->server
, FLAG_MAP
))))
446 && ((!(matchsel
& WHO_FIELD_HOS
))
447 || matchexec(cli_user(acptr
)->host
, mymask
, minlen
))
448 && ((!(matchsel
& WHO_FIELD_HOS
))
449 || !HasSetHost(acptr
)
450 || !HasHiddenHost(acptr
)
452 || matchexec(cli_user(acptr
)->realhost
, mymask
, minlen
))
453 && ((!(matchsel
& WHO_FIELD_REN
))
454 || matchexec(cli_info(acptr
), mymask
, minlen
))
455 && ((!(matchsel
& WHO_FIELD_NIP
))
456 || ((HasHiddenHost(acptr
) || HasSetHost(acptr
)) && !IsAnOper(sptr
))
457 || !ipmask_check(&cli_ip(acptr
), &imask
, ibits
))
458 && ((!(matchsel
& WHO_FIELD_ACC
))
459 || matchexec(cli_user(acptr
)->account
, mymask
, minlen
)))
461 if (!SHOW_MORE(sptr
, counter
))
463 do_who(sptr
, acptr
, 0, fields
, qrt
);
467 /* Make a clean mask suitable to be sent in the "end of" */
468 if (mask
&& (p
= strchr(mask
, ' ')))
470 send_reply(sptr
, RPL_ENDOFWHO
, BadPtr(mask
) ? "*" : mask
);
472 /* Notify the user if we decided that his query was too long */
474 send_reply(sptr
, ERR_QUERYTOOLONG
, "WHO");