Author: Isomer <isomer@coders.net>
[ircu2.10.12-pk.git] / ircd / m_who.c
1 /*
2  * IRC - Internet Relay Chat, ircd/m_who.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  *
6  * See file AUTHORS in IRC package for additional names of
7  * the programmers.
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 1, or (at your option)
12  * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * $Id$
24  */
25
26 /*
27  * m_functions execute protocol messages on this server:
28  *
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...).
34  *
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.
38  *
39  *            (!IsServer(cptr)) => (cptr == sptr), because
40  *            prefixes are taken *only* from servers...
41  *
42  *            (IsServer(cptr))
43  *                    (sptr == cptr) => the message didn't
44  *                    have the prefix.
45  *
46  *                    (sptr != cptr && IsServer(sptr) means
47  *                    the prefix specified servername. (?)
48  *
49  *                    (sptr != cptr && !IsServer(sptr) means
50  *                    that message originated from a remote
51  *                    user (not local).
52  *
53  *            combining
54  *
55  *            (!IsServer(sptr)) means that, sptr can safely
56  *            taken as defining the target structure of the
57  *            message in this server.
58  *
59  *    *Always* true (if 'parse' and others are working correct):
60  *
61  *    1)      sptr->from == cptr  (note: cptr->from == cptr)
62  *
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 ]
67  *
68  *    parc    number of variable parameter strings (if zero,
69  *            parv is allowed to be NULL)
70  *
71  *    parv    a NULL terminated list of parameter pointers,
72  *
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*
78  *
79  *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
80  *                    non-NULL pointers.
81  */
82 #if 0
83 /*
84  * No need to include handlers.h here the signatures must match
85  * and we don't need to force a rebuild of all the handlers everytime
86  * we add a new one to the list. --Bleep
87  */
88 #include "handlers.h"
89 #endif /* 0 */
90 #include "channel.h"
91 #include "client.h"
92 #include "hash.h"
93 #include "ircd.h"
94 #include "ircd_chattr.h"
95 #include "ircd_log.h"
96 #include "ircd_reply.h"
97 #include "ircd_string.h"
98 #include "match.h"
99 #include "numeric.h"
100 #include "numnicks.h"
101 #include "send.h"
102 #include "support.h"
103 #include "whocmds.h"
104
105
106 #include <assert.h>
107 #include <string.h>
108
109
110 /*
111  * A little spin-marking utility to tell us wich clients we have already
112  * processed and wich not
113  */
114 static int who_marker = 0;
115 static void move_marker(void)
116 {
117   if (!++who_marker)
118   {
119     struct Client *cptr = GlobalClientList;
120     while (cptr)
121     {
122       cptr->marker = 0;
123       cptr = cptr->next;
124     }
125     who_marker++;
126   }
127 }
128
129 #define CheckMark(x, y) ((x == y) ? 0 : (x = y))
130 #define Process(cptr) CheckMark(cptr->marker, who_marker)
131
132 /*
133  * m_who - generic message handler
134  *
135  *  parv[0] = sender prefix
136  *  parv[1] = nickname mask list
137  *  parv[2] = additional selection flag, only 'o' for now.
138  *            and %flags to specify what fields to output
139  *            plus a ,querytype if the t flag is specified
140  *            so the final thing will be like o%tnchu,777
141  *  parv[3] = _optional_ parameter that overrides parv[1]
142  *            This can be used as "/quote who foo % :The Black Hacker
143  *            to find me, parv[3] _can_ contain spaces !.
144  */
145 int m_who(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
146 {
147   char *mask;           /* The mask we are looking for              */
148   char ch;                      /* Scratch char register                    */
149   struct Channel *chptr;                /* Channel to show                          */
150   struct Client *acptr;         /* Client to show                           */
151
152   int bitsel;                   /* Mask of selectors to apply               */
153   int matchsel;                 /* Wich fields the match should apply on    */
154   int counter;                  /* Query size counter,
155                                    initially used to count fields           */
156   int commas;                   /* Does our mask contain any comma ?
157                                    If so is a list..                        */
158   int fields;                   /* Mask of fields to show                   */
159   int isthere = 0;              /* When this set the user is member of chptr */
160   char *nick;                   /* Single element extracted from
161                                    the mask list                            */
162   char *p;                      /* Scratch char pointer                     */
163   char *qrt;                    /* Pointer to the query type                */
164   static char mymask[512];      /* To save the mask before corrupting it    */
165
166   /* Let's find where is our mask, and if actually contains something */
167   mask = ((parc > 1) ? parv[1] : 0);
168   if (parc > 3 && parv[3])
169     mask = parv[3];
170   if (mask && ((mask[0] == '\0') ||
171       (mask[1] == '\0' && ((mask[0] == '0') || (mask[0] == '*')))))
172     mask = 0;
173
174   /* Evaluate the flags now, we consider the second parameter 
175      as "matchFlags%fieldsToInclude,querytype"           */
176   bitsel = fields = counter = matchsel = 0;
177   qrt = 0;
178   if (parc > 2 && parv[2] && *parv[2])
179   {
180     p = parv[2];
181     while (((ch = *(p++))) && (ch != '%') && (ch != ','))
182       switch (ch)
183       {
184         case 'o':
185         case 'O':
186           bitsel |= WHOSELECT_OPER;
187           continue;
188         case 'x':
189         case 'X':
190           bitsel |= WHOSELECT_EXTRA;
191 #ifdef WPATH
192           if (IsAnOper(sptr))
193             write_log(WPATH, "# " TIME_T_FMT " %s!%s@%s WHO %s %s\n",
194                 CurrentTime, sptr->name, sptr->user->username, sptr->user->host,
195                 (BadPtr(parv[3]) ? parv[1] : parv[3]), parv[2]);
196 #endif /* WPATH */
197           continue;
198         case 'n':
199         case 'N':
200           matchsel |= WHO_FIELD_NIC;
201           continue;
202         case 'u':
203         case 'U':
204           matchsel |= WHO_FIELD_UID;
205           continue;
206         case 'h':
207         case 'H':
208           matchsel |= WHO_FIELD_HOS;
209           continue;
210         case 'i':
211         case 'I':
212           matchsel |= WHO_FIELD_NIP;
213           continue;
214         case 's':
215         case 'S':
216           matchsel |= WHO_FIELD_SER;
217           continue;
218         case 'r':
219         case 'R':
220           matchsel |= WHO_FIELD_REN;
221           continue;
222       }
223     if (ch == '%')
224       while ((ch = *p++) && (ch != ','))
225       {
226         counter++;
227         switch (ch)
228         {
229           case 'c':
230           case 'C':
231             fields |= WHO_FIELD_CHA;
232             break;
233           case 'd':
234           case 'D':
235             fields |= WHO_FIELD_DIS;
236             break;
237           case 'f':
238           case 'F':
239             fields |= WHO_FIELD_FLA;
240             break;
241           case 'h':
242           case 'H':
243             fields |= WHO_FIELD_HOS;
244             break;
245           case 'i':
246           case 'I':
247             fields |= WHO_FIELD_NIP;
248             break;
249           case 'l':
250           case 'L':
251             fields |= WHO_FIELD_IDL;
252           case 'n':
253           case 'N':
254             fields |= WHO_FIELD_NIC;
255             break;
256           case 'r':
257           case 'R':
258             fields |= WHO_FIELD_REN;
259             break;
260           case 's':
261           case 'S':
262             fields |= WHO_FIELD_SER;
263             break;
264           case 't':
265           case 'T':
266             fields |= WHO_FIELD_QTY;
267             break;
268           case 'u':
269           case 'U':
270             fields |= WHO_FIELD_UID;
271             break;
272           default:
273             break;
274         }
275       };
276     if (ch)
277       qrt = p;
278   }
279
280   if (!matchsel)
281     matchsel = WHO_FIELD_DEF;
282   if (!fields)
283     counter = 7;
284
285   if (qrt && (fields & WHO_FIELD_QTY))
286   {
287     p = qrt;
288     if (!((*p > '9') || (*p < '0')))
289       p++;
290     if (!((*p > '9') || (*p < '0')))
291       p++;
292     if (!((*p > '9') || (*p < '0')))
293       p++;
294     *p = '\0';
295   }
296   else
297     qrt = 0;
298
299   /* I'd love to add also a check on the number of matches fields per time */
300   counter = (2048 / (counter + 4));
301   if (mask && (strlen(mask) > 510))
302     mask[510] = '\0';
303   move_marker();
304   commas = (mask && strchr(mask, ','));
305
306   /* First treat mask as a list of plain nicks/channels */
307   if (mask)
308   {
309     strcpy(mymask, mask);
310     for (p = 0, nick = ircd_strtok(&p, mymask, ","); nick;
311         nick = ircd_strtok(&p, 0, ","))
312     {
313       if (IsChannelName(nick) && (chptr = FindChannel(nick)))
314       {
315         isthere = (find_channel_member(sptr, chptr) != 0);
316         if (isthere || SEE_CHANNEL(sptr, chptr, bitsel))
317         {
318           struct Membership* member;
319           for (member = chptr->members; member; member = member->next_member)
320           {
321             acptr = member->user;
322             if ((bitsel & WHOSELECT_OPER) && !(IsAnOper(acptr)))
323               continue;
324             if ((acptr != sptr) && (member->status & CHFL_ZOMBIE))
325               continue;
326             if (!(isthere || (SEE_USER(sptr, acptr, bitsel))))
327               continue;
328             if (!Process(acptr))        /* This can't be moved before other checks */
329               continue;
330             if (!(isthere || (SHOW_MORE(sptr, counter))))
331               break;
332             do_who(sptr, acptr, chptr, fields, qrt);
333           }
334         }
335       }
336       else
337       {
338         if ((acptr = FindUser(nick)) &&
339             ((!(bitsel & WHOSELECT_OPER)) || IsAnOper(acptr)) &&
340             Process(acptr) && SHOW_MORE(sptr, counter))
341         {
342           do_who(sptr, acptr, 0, fields, qrt);
343         }
344       }
345     }
346   }
347
348   /* If we didn't have any comma in the mask treat it as a
349      real mask and try to match all relevant fields */
350   if (!(commas || (counter < 1)))
351   {
352     int minlen, cset;
353     static struct in_mask imask;
354     if (mask)
355     {
356       matchcomp(mymask, &minlen, &cset, mask);
357       if (matchcompIP(&imask, mask))
358         matchsel &= ~WHO_FIELD_NIP;
359       if ((minlen > NICKLEN) || !(cset & NTL_IRCNK))
360         matchsel &= ~WHO_FIELD_NIC;
361       if ((matchsel & WHO_FIELD_SER) &&
362           ((minlen > HOSTLEN) || (!(cset & NTL_IRCHN))
363           || (!markMatchexServer(mymask, minlen))))
364         matchsel &= ~WHO_FIELD_SER;
365       if ((minlen > USERLEN) || !(cset & NTL_IRCUI))
366         matchsel &= ~WHO_FIELD_UID;
367       if ((minlen > HOSTLEN) || !(cset & NTL_IRCHN))
368         matchsel &= ~WHO_FIELD_HOS;
369     }
370
371     /* First of all loop through the clients in common channels */
372     if ((!(counter < 1)) && matchsel) {
373       struct Membership* member;
374       struct Membership* chan;
375       for (chan = sptr->user->channel; chan; chan = chan->next_channel) {
376         chptr = chan->channel;
377         for (member = chptr->members; member; member = member->next_member)
378         {
379           acptr = member->user;
380           if (!(IsUser(acptr) && Process(acptr)))
381             continue;           /* Now Process() is at the beginning, if we fail
382                                    we'll never have to show this acptr in this query */
383           if ((bitsel & WHOSELECT_OPER) && !IsAnOper(acptr))
384             continue;
385           if ((mask) &&
386               ((!(matchsel & WHO_FIELD_NIC))
387               || matchexec(acptr->name, mymask, minlen))
388               && ((!(matchsel & WHO_FIELD_UID))
389               || matchexec(acptr->user->username, mymask, minlen))
390               && ((!(matchsel & WHO_FIELD_SER))
391               || (!(acptr->user->server->flags & FLAGS_MAP)))
392               && ((!(matchsel & WHO_FIELD_HOS))
393               || matchexec(acptr->user->host, mymask, minlen))
394               && ((!(matchsel & WHO_FIELD_REN))
395               || matchexec(acptr->info, mymask, minlen))
396               && ((!(matchsel & WHO_FIELD_NIP))
397               || ((((acptr->ip.s_addr & imask.mask.s_addr) !=
398               imask.bits.s_addr)) || (imask.fall
399               && matchexec(ircd_ntoa((const char*) &acptr->ip), mymask, minlen)))))
400             continue;
401           if (!SHOW_MORE(sptr, counter))
402             break;
403           do_who(sptr, acptr, chptr, fields, qrt);
404         }
405       }
406     }
407     /* Loop through all clients :-\, if we still have something to match to 
408        and we can show more clients */
409     if ((!(counter < 1)) && matchsel)
410       for (acptr = me.prev; acptr; acptr = acptr->prev)
411       {
412         if (!(IsUser(acptr) && Process(acptr)))
413           continue;
414         if ((bitsel & WHOSELECT_OPER) && !IsAnOper(acptr))
415           continue;
416         if (!(SEE_USER(sptr, acptr, bitsel)))
417           continue;
418         if ((mask) &&
419             ((!(matchsel & WHO_FIELD_NIC))
420             || matchexec(acptr->name, mymask, minlen))
421             && ((!(matchsel & WHO_FIELD_UID))
422             || matchexec(acptr->user->username, mymask, minlen))
423             && ((!(matchsel & WHO_FIELD_SER))
424             || (!(acptr->user->server->flags & FLAGS_MAP)))
425             && ((!(matchsel & WHO_FIELD_HOS))
426             || matchexec(acptr->user->host, mymask, minlen))
427             && ((!(matchsel & WHO_FIELD_REN))
428             || matchexec(acptr->info, mymask, minlen))
429             && ((!(matchsel & WHO_FIELD_NIP))
430             || ((((acptr->ip.s_addr & imask.mask.s_addr) != imask.bits.s_addr))
431             || (imask.fall
432             && matchexec(ircd_ntoa((const char*) &acptr->ip), mymask, minlen)))))
433           continue;
434         if (!SHOW_MORE(sptr, counter))
435           break;
436         do_who(sptr, acptr, 0, fields, qrt);
437       }
438   }
439
440   /* Make a clean mask suitable to be sent in the "end of" */
441   if (mask && (p = strchr(mask, ' ')))
442     *p = '\0';
443   sendto_one(sptr, rpl_str(RPL_ENDOFWHO),
444       me.name, parv[0], BadPtr(mask) ? "*" : mask);
445
446   /* Notify the user if we decided that his query was too long */
447   if (counter < 0)
448     sendto_one(sptr, err_str(ERR_QUERYTOOLONG), me.name, parv[0], "WHO");
449
450   return 0;
451 }
452
453
454 #if 0
455 /*
456  *  m_who
457  *
458  *  parv[0] = sender prefix
459  *  parv[1] = nickname mask list
460  *  parv[2] = additional selection flag, only 'o' for now.
461  *            and %flags to specify what fields to output
462  *            plus a ,querytype if the t flag is specified
463  *            so the final thing will be like o%tnchu,777
464  *  parv[3] = _optional_ parameter that overrides parv[1]
465  *            This can be used as "/quote who foo % :The Black Hacker
466  *            to find me, parv[3] _can_ contain spaces !.
467  */
468
469 int m_who(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
470 {
471   char *mask;           /* The mask we are looking for              */
472   char ch;                      /* Scratch char register                    */
473   struct Channel *chptr;                /* Channel to show                          */
474   struct Client *acptr;         /* Client to show                           */
475
476   int bitsel;                   /* Mask of selectors to apply               */
477   int matchsel;                 /* Wich fields the match should apply on    */
478   int counter;                  /* Query size counter,
479                                    initially used to count fields           */
480   int commas;                   /* Does our mask contain any comma ?
481                                    If so is a list..                        */
482   int fields;                   /* Mask of fields to show                   */
483   int isthere = 0;              /* When this set the user is member of chptr */
484   char *nick;                   /* Single element extracted from
485                                    the mask list                            */
486   char *p;                      /* Scratch char pointer                     */
487   char *qrt;                    /* Pointer to the query type                */
488   static char mymask[512];      /* To save the mask before corrupting it    */
489
490   /* Let's find where is our mask, and if actually contains something */
491   mask = ((parc > 1) ? parv[1] : 0);
492   if (parc > 3 && parv[3])
493     mask = parv[3];
494   if (mask && ((mask[0] == '\0') ||
495       (mask[1] == '\0' && ((mask[0] == '0') || (mask[0] == '*')))))
496     mask = 0;
497
498   /* Evaluate the flags now, we consider the second parameter 
499      as "matchFlags%fieldsToInclude,querytype"           */
500   bitsel = fields = counter = matchsel = 0;
501   qrt = 0;
502   if (parc > 2 && parv[2] && *parv[2])
503   {
504     p = parv[2];
505     while (((ch = *(p++))) && (ch != '%') && (ch != ','))
506       switch (ch)
507       {
508         case 'o':
509         case 'O':
510           bitsel |= WHOSELECT_OPER;
511           continue;
512         case 'x':
513         case 'X':
514           bitsel |= WHOSELECT_EXTRA;
515 #ifdef WPATH
516           if (IsAnOper(sptr))
517             write_log(WPATH, "# " TIME_T_FMT " %s!%s@%s WHO %s %s\n",
518                 CurrentTime, sptr->name, sptr->user->username, sptr->user->host,
519                 (BadPtr(parv[3]) ? parv[1] : parv[3]), parv[2]);
520 #endif /* WPATH */
521           continue;
522         case 'n':
523         case 'N':
524           matchsel |= WHO_FIELD_NIC;
525           continue;
526         case 'u':
527         case 'U':
528           matchsel |= WHO_FIELD_UID;
529           continue;
530         case 'h':
531         case 'H':
532           matchsel |= WHO_FIELD_HOS;
533           continue;
534         case 'i':
535         case 'I':
536           matchsel |= WHO_FIELD_NIP;
537           continue;
538         case 's':
539         case 'S':
540           matchsel |= WHO_FIELD_SER;
541           continue;
542         case 'r':
543         case 'R':
544           matchsel |= WHO_FIELD_REN;
545           continue;
546       }
547     if (ch == '%')
548       while ((ch = *p++) && (ch != ','))
549       {
550         counter++;
551         switch (ch)
552         {
553           case 'c':
554           case 'C':
555             fields |= WHO_FIELD_CHA;
556             break;
557           case 'd':
558           case 'D':
559             fields |= WHO_FIELD_DIS;
560             break;
561           case 'f':
562           case 'F':
563             fields |= WHO_FIELD_FLA;
564             break;
565           case 'h':
566           case 'H':
567             fields |= WHO_FIELD_HOS;
568             break;
569           case 'i':
570           case 'I':
571             fields |= WHO_FIELD_NIP;
572             break;
573           case 'n':
574           case 'N':
575             fields |= WHO_FIELD_NIC;
576             break;
577           case 'r':
578           case 'R':
579             fields |= WHO_FIELD_REN;
580             break;
581           case 's':
582           case 'S':
583             fields |= WHO_FIELD_SER;
584             break;
585           case 't':
586           case 'T':
587             fields |= WHO_FIELD_QTY;
588             break;
589           case 'u':
590           case 'U':
591             fields |= WHO_FIELD_UID;
592             break;
593           default:
594             break;
595         }
596       };
597     if (ch)
598       qrt = p;
599   }
600
601   if (!matchsel)
602     matchsel = WHO_FIELD_DEF;
603   if (!fields)
604     counter = 7;
605
606   if (qrt && (fields & WHO_FIELD_QTY))
607   {
608     p = qrt;
609     if (!((*p > '9') || (*p < '0')))
610       p++;
611     if (!((*p > '9') || (*p < '0')))
612       p++;
613     if (!((*p > '9') || (*p < '0')))
614       p++;
615     *p = '\0';
616   }
617   else
618     qrt = 0;
619
620   /* I'd love to add also a check on the number of matches fields per time */
621   counter = (2048 / (counter + 4));
622   if (mask && (strlen(mask) > 510))
623     mask[510] = '\0';
624   move_marker();
625   commas = (mask && strchr(mask, ','));
626
627   /* First treat mask as a list of plain nicks/channels */
628   if (mask)
629   {
630     strcpy(mymask, mask);
631     for (p = 0, nick = ircd_strtok(&p, mymask, ","); nick;
632         nick = ircd_strtok(&p, 0, ","))
633     {
634       if (IsChannelName(nick) && (chptr = FindChannel(nick)))
635       {
636         isthere = (find_channel_member(sptr, chptr) != 0);
637         if (isthere || SEE_CHANNEL(sptr, chptr, bitsel))
638         {
639           struct Membership* member;
640           for (member = chptr->members; member; member = member->next_member)
641           {
642             acptr = member->user;
643             if ((bitsel & WHOSELECT_OPER) && !(IsAnOper(acptr)))
644               continue;
645             if ((acptr != sptr) && (member->status & CHFL_ZOMBIE))
646               continue;
647             if (!(isthere || (SEE_USER(sptr, acptr, bitsel))))
648               continue;
649             if (!Process(acptr))        /* This can't be moved before other checks */
650               continue;
651             if (!(isthere || (SHOW_MORE(sptr, counter))))
652               break;
653             do_who(sptr, acptr, chptr, fields, qrt);
654           }
655         }
656       }
657       else
658       {
659         if ((acptr = FindUser(nick)) &&
660             ((!(bitsel & WHOSELECT_OPER)) || IsAnOper(acptr)) &&
661             Process(acptr) && SHOW_MORE(sptr, counter))
662         {
663           do_who(sptr, acptr, 0, fields, qrt);
664         }
665       }
666     }
667   }
668
669   /* If we didn't have any comma in the mask treat it as a
670      real mask and try to match all relevant fields */
671   if (!(commas || (counter < 1)))
672   {
673     int minlen, cset;
674     static struct in_mask imask;
675     if (mask)
676     {
677       matchcomp(mymask, &minlen, &cset, mask);
678       if (matchcompIP(&imask, mask))
679         matchsel &= ~WHO_FIELD_NIP;
680       if ((minlen > NICKLEN) || !(cset & NTL_IRCNK))
681         matchsel &= ~WHO_FIELD_NIC;
682       if ((matchsel & WHO_FIELD_SER) &&
683           ((minlen > HOSTLEN) || (!(cset & NTL_IRCHN))
684           || (!markMatchexServer(mymask, minlen))))
685         matchsel &= ~WHO_FIELD_SER;
686       if ((minlen > USERLEN) || !(cset & NTL_IRCUI))
687         matchsel &= ~WHO_FIELD_UID;
688       if ((minlen > HOSTLEN) || !(cset & NTL_IRCHN))
689         matchsel &= ~WHO_FIELD_HOS;
690     }
691
692     /* First of all loop through the clients in common channels */
693     if ((!(counter < 1)) && matchsel) {
694       struct Membership* member;
695       struct Membership* chan;
696       for (chan = sptr->user->channel; chan; chan = chan->next_channel) {
697         chptr = chan->channel;
698         for (member = chptr->members; member; member = member->next_member)
699         {
700           acptr = member->user;
701           if (!(IsUser(acptr) && Process(acptr)))
702             continue;           /* Now Process() is at the beginning, if we fail
703                                    we'll never have to show this acptr in this query */
704           if ((bitsel & WHOSELECT_OPER) && !IsAnOper(acptr))
705             continue;
706           if ((mask) &&
707               ((!(matchsel & WHO_FIELD_NIC))
708               || matchexec(acptr->name, mymask, minlen))
709               && ((!(matchsel & WHO_FIELD_UID))
710               || matchexec(acptr->user->username, mymask, minlen))
711               && ((!(matchsel & WHO_FIELD_SER))
712               || (!(acptr->user->server->flags & FLAGS_MAP)))
713               && ((!(matchsel & WHO_FIELD_HOS))
714               || matchexec(acptr->user->host, mymask, minlen))
715               && ((!(matchsel & WHO_FIELD_REN))
716               || matchexec(acptr->info, mymask, minlen))
717               && ((!(matchsel & WHO_FIELD_NIP))
718               || ((((acptr->ip.s_addr & imask.mask.s_addr) !=
719               imask.bits.s_addr)) || (imask.fall
720               && matchexec(inet_ntoa(acptr->ip), mymask, minlen)))))
721             continue;
722           if (!SHOW_MORE(sptr, counter))
723             break;
724           do_who(sptr, acptr, chptr, fields, qrt);
725         }
726       }
727     }
728     /* Loop through all clients :-\, if we still have something to match to 
729        and we can show more clients */
730     if ((!(counter < 1)) && matchsel)
731       for (acptr = me.prev; acptr; acptr = acptr->prev)
732       {
733         if (!(IsUser(acptr) && Process(acptr)))
734           continue;
735         if ((bitsel & WHOSELECT_OPER) && !IsAnOper(acptr))
736           continue;
737         if (!(SEE_USER(sptr, acptr, bitsel)))
738           continue;
739         if ((mask) &&
740             ((!(matchsel & WHO_FIELD_NIC))
741             || matchexec(acptr->name, mymask, minlen))
742             && ((!(matchsel & WHO_FIELD_UID))
743             || matchexec(acptr->user->username, mymask, minlen))
744             && ((!(matchsel & WHO_FIELD_SER))
745             || (!(acptr->user->server->flags & FLAGS_MAP)))
746             && ((!(matchsel & WHO_FIELD_HOS))
747             || matchexec(acptr->user->host, mymask, minlen))
748             && ((!(matchsel & WHO_FIELD_REN))
749             || matchexec(acptr->info, mymask, minlen))
750             && ((!(matchsel & WHO_FIELD_NIP))
751             || ((((acptr->ip.s_addr & imask.mask.s_addr) != imask.bits.s_addr))
752             || (imask.fall
753             && matchexec(inet_ntoa(acptr->ip), mymask, minlen)))))
754           continue;
755         if (!SHOW_MORE(sptr, counter))
756           break;
757         do_who(sptr, acptr, 0, fields, qrt);
758       }
759   }
760
761   /* Make a clean mask suitable to be sent in the "end of" */
762   if (mask && (p = strchr(mask, ' ')))
763     *p = '\0';
764   sendto_one(sptr, rpl_str(RPL_ENDOFWHO),
765       me.name, parv[0], BadPtr(mask) ? "*" : mask);
766
767   /* Notify the user if we decided that his query was too long */
768   if (counter < 0)
769     sendto_one(sptr, err_str(ERR_QUERYTOOLONG), me.name, parv[0], "WHO");
770
771   return 0;
772 }
773 #endif /* 0 */
774