Author: Kev <klmitch@mit.edu>
[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       cli_marker(cptr) = 0;
123       cptr = cli_next(cptr);
124     }
125     who_marker++;
126   }
127 }
128
129 #define CheckMark(x, y) ((x == y) ? 0 : (x = y))
130 #define Process(cptr) CheckMark(cli_marker(cptr), 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           if (IsAnOper(sptr))
192             log_write(LS_WHO, L_INFO, LOG_NOSNOTICE, "%#C WHO %s %s", sptr,
193                       (BadPtr(parv[3]) ? parv[1] : parv[3]), parv[2]);
194           continue;
195         case 'n':
196         case 'N':
197           matchsel |= WHO_FIELD_NIC;
198           continue;
199         case 'u':
200         case 'U':
201           matchsel |= WHO_FIELD_UID;
202           continue;
203         case 'h':
204         case 'H':
205           matchsel |= WHO_FIELD_HOS;
206           continue;
207         case 'i':
208         case 'I':
209           matchsel |= WHO_FIELD_NIP;
210           continue;
211         case 's':
212         case 'S':
213           matchsel |= WHO_FIELD_SER;
214           continue;
215         case 'r':
216         case 'R':
217           matchsel |= WHO_FIELD_REN;
218           continue;
219       }
220     if (ch == '%')
221       while ((ch = *p++) && (ch != ','))
222       {
223         counter++;
224         switch (ch)
225         {
226           case 'c':
227           case 'C':
228             fields |= WHO_FIELD_CHA;
229             break;
230           case 'd':
231           case 'D':
232             fields |= WHO_FIELD_DIS;
233             break;
234           case 'f':
235           case 'F':
236             fields |= WHO_FIELD_FLA;
237             break;
238           case 'h':
239           case 'H':
240             fields |= WHO_FIELD_HOS;
241             break;
242           case 'i':
243           case 'I':
244             fields |= WHO_FIELD_NIP;
245             break;
246           case 'l':
247           case 'L':
248             fields |= WHO_FIELD_IDL;
249           case 'n':
250           case 'N':
251             fields |= WHO_FIELD_NIC;
252             break;
253           case 'r':
254           case 'R':
255             fields |= WHO_FIELD_REN;
256             break;
257           case 's':
258           case 'S':
259             fields |= WHO_FIELD_SER;
260             break;
261           case 't':
262           case 'T':
263             fields |= WHO_FIELD_QTY;
264             break;
265           case 'u':
266           case 'U':
267             fields |= WHO_FIELD_UID;
268             break;
269           default:
270             break;
271         }
272       };
273     if (ch)
274       qrt = p;
275   }
276
277   if (!matchsel)
278     matchsel = WHO_FIELD_DEF;
279   if (!fields)
280     counter = 7;
281
282   if (qrt && (fields & WHO_FIELD_QTY))
283   {
284     p = qrt;
285     if (!((*p > '9') || (*p < '0')))
286       p++;
287     if (!((*p > '9') || (*p < '0')))
288       p++;
289     if (!((*p > '9') || (*p < '0')))
290       p++;
291     *p = '\0';
292   }
293   else
294     qrt = 0;
295
296   /* I'd love to add also a check on the number of matches fields per time */
297   counter = (2048 / (counter + 4));
298   if (mask && (strlen(mask) > 510))
299     mask[510] = '\0';
300   move_marker();
301   commas = (mask && strchr(mask, ','));
302
303   /* First treat mask as a list of plain nicks/channels */
304   if (mask)
305   {
306     strcpy(mymask, mask);
307     for (p = 0, nick = ircd_strtok(&p, mymask, ","); nick;
308         nick = ircd_strtok(&p, 0, ","))
309     {
310       if (IsChannelName(nick) && (chptr = FindChannel(nick)))
311       {
312         isthere = (find_channel_member(sptr, chptr) != 0);
313         if (isthere || SEE_CHANNEL(sptr, chptr, bitsel))
314         {
315           struct Membership* member;
316           for (member = chptr->members; member; member = member->next_member)
317           {
318             acptr = member->user;
319             if ((bitsel & WHOSELECT_OPER) && !(IsAnOper(acptr)))
320               continue;
321             if ((acptr != sptr) && (member->status & CHFL_ZOMBIE))
322               continue;
323             if (!(isthere || (SEE_USER(sptr, acptr, bitsel))))
324               continue;
325             if (!Process(acptr))        /* This can't be moved before other checks */
326               continue;
327             if (!(isthere || (SHOW_MORE(sptr, counter))))
328               break;
329             do_who(sptr, acptr, chptr, fields, qrt);
330           }
331         }
332       }
333       else
334       {
335         if ((acptr = FindUser(nick)) &&
336             ((!(bitsel & WHOSELECT_OPER)) || IsAnOper(acptr)) &&
337             Process(acptr) && SHOW_MORE(sptr, counter))
338         {
339           do_who(sptr, acptr, 0, fields, qrt);
340         }
341       }
342     }
343   }
344
345   /* If we didn't have any comma in the mask treat it as a
346      real mask and try to match all relevant fields */
347   if (!(commas || (counter < 1)))
348   {
349     int minlen, cset;
350     static struct in_mask imask;
351     if (mask)
352     {
353       matchcomp(mymask, &minlen, &cset, mask);
354       if (matchcompIP(&imask, mask))
355         matchsel &= ~WHO_FIELD_NIP;
356       if ((minlen > NICKLEN) || !(cset & NTL_IRCNK))
357         matchsel &= ~WHO_FIELD_NIC;
358       if ((matchsel & WHO_FIELD_SER) &&
359           ((minlen > HOSTLEN) || (!(cset & NTL_IRCHN))
360           || (!markMatchexServer(mymask, minlen))))
361         matchsel &= ~WHO_FIELD_SER;
362       if ((minlen > USERLEN) || !(cset & NTL_IRCUI))
363         matchsel &= ~WHO_FIELD_UID;
364       if ((minlen > HOSTLEN) || !(cset & NTL_IRCHN))
365         matchsel &= ~WHO_FIELD_HOS;
366     }
367
368     /* First of all loop through the clients in common channels */
369     if ((!(counter < 1)) && matchsel) {
370       struct Membership* member;
371       struct Membership* chan;
372       for (chan = cli_user(sptr)->channel; chan; chan = chan->next_channel) {
373         chptr = chan->channel;
374         for (member = chptr->members; member; member = member->next_member)
375         {
376           acptr = member->user;
377           if (!(IsUser(acptr) && Process(acptr)))
378             continue;           /* Now Process() is at the beginning, if we fail
379                                    we'll never have to show this acptr in this query */
380           if ((bitsel & WHOSELECT_OPER) && !IsAnOper(acptr))
381             continue;
382           if ((mask) &&
383               ((!(matchsel & WHO_FIELD_NIC))
384               || matchexec(cli_name(acptr), mymask, minlen))
385               && ((!(matchsel & WHO_FIELD_UID))
386               || matchexec(cli_user(acptr)->username, mymask, minlen))
387               && ((!(matchsel & WHO_FIELD_SER))
388               || (!(cli_flags(cli_user(acptr)->server) & FLAGS_MAP)))
389               && ((!(matchsel & WHO_FIELD_HOS))
390               || matchexec(cli_user(acptr)->host, mymask, minlen))
391               && ((!(matchsel & WHO_FIELD_REN))
392               || matchexec(cli_info(acptr), mymask, minlen))
393               && ((!(matchsel & WHO_FIELD_NIP))
394               || ((((cli_ip(acptr).s_addr & imask.mask.s_addr) !=
395               imask.bits.s_addr)) || (imask.fall
396               && matchexec(ircd_ntoa((const char*) &(cli_ip(acptr))), mymask, minlen)))))
397             continue;
398           if (!SHOW_MORE(sptr, counter))
399             break;
400           do_who(sptr, acptr, chptr, fields, qrt);
401         }
402       }
403     }
404     /* Loop through all clients :-\, if we still have something to match to 
405        and we can show more clients */
406     if ((!(counter < 1)) && matchsel)
407       for (acptr = cli_prev(&me); acptr; acptr = cli_prev(acptr))
408       {
409         if (!(IsUser(acptr) && Process(acptr)))
410           continue;
411         if ((bitsel & WHOSELECT_OPER) && !IsAnOper(acptr))
412           continue;
413         if (!(SEE_USER(sptr, acptr, bitsel)))
414           continue;
415         if ((mask) &&
416             ((!(matchsel & WHO_FIELD_NIC))
417             || matchexec(cli_name(acptr), mymask, minlen))
418             && ((!(matchsel & WHO_FIELD_UID))
419             || matchexec(cli_user(acptr)->username, mymask, minlen))
420             && ((!(matchsel & WHO_FIELD_SER))
421             || (!(cli_flags(cli_user(acptr)->server) & FLAGS_MAP)))
422             && ((!(matchsel & WHO_FIELD_HOS))
423             || matchexec(cli_user(acptr)->host, mymask, minlen))
424             && ((!(matchsel & WHO_FIELD_REN))
425             || matchexec(cli_info(acptr), mymask, minlen))
426             && ((!(matchsel & WHO_FIELD_NIP))
427             || ((((cli_ip(acptr).s_addr & imask.mask.s_addr) != imask.bits.s_addr))
428             || (imask.fall
429             && matchexec(ircd_ntoa((const char*) &(cli_ip(acptr))), mymask, minlen)))))
430           continue;
431         if (!SHOW_MORE(sptr, counter))
432           break;
433         do_who(sptr, acptr, 0, fields, qrt);
434       }
435   }
436
437   /* Make a clean mask suitable to be sent in the "end of" */
438   if (mask && (p = strchr(mask, ' ')))
439     *p = '\0';
440   send_reply(sptr, RPL_ENDOFWHO, BadPtr(mask) ? "*" : mask);
441
442   /* Notify the user if we decided that his query was too long */
443   if (counter < 0)
444     send_reply(sptr, ERR_QUERYTOOLONG, "WHO");
445
446   return 0;
447 }
448
449
450 #if 0
451 /*
452  *  m_who
453  *
454  *  parv[0] = sender prefix
455  *  parv[1] = nickname mask list
456  *  parv[2] = additional selection flag, only 'o' for now.
457  *            and %flags to specify what fields to output
458  *            plus a ,querytype if the t flag is specified
459  *            so the final thing will be like o%tnchu,777
460  *  parv[3] = _optional_ parameter that overrides parv[1]
461  *            This can be used as "/quote who foo % :The Black Hacker
462  *            to find me, parv[3] _can_ contain spaces !.
463  */
464
465 int m_who(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
466 {
467   char *mask;           /* The mask we are looking for              */
468   char ch;                      /* Scratch char register                    */
469   struct Channel *chptr;                /* Channel to show                          */
470   struct Client *acptr;         /* Client to show                           */
471
472   int bitsel;                   /* Mask of selectors to apply               */
473   int matchsel;                 /* Wich fields the match should apply on    */
474   int counter;                  /* Query size counter,
475                                    initially used to count fields           */
476   int commas;                   /* Does our mask contain any comma ?
477                                    If so is a list..                        */
478   int fields;                   /* Mask of fields to show                   */
479   int isthere = 0;              /* When this set the user is member of chptr */
480   char *nick;                   /* Single element extracted from
481                                    the mask list                            */
482   char *p;                      /* Scratch char pointer                     */
483   char *qrt;                    /* Pointer to the query type                */
484   static char mymask[512];      /* To save the mask before corrupting it    */
485
486   /* Let's find where is our mask, and if actually contains something */
487   mask = ((parc > 1) ? parv[1] : 0);
488   if (parc > 3 && parv[3])
489     mask = parv[3];
490   if (mask && ((mask[0] == '\0') ||
491       (mask[1] == '\0' && ((mask[0] == '0') || (mask[0] == '*')))))
492     mask = 0;
493
494   /* Evaluate the flags now, we consider the second parameter 
495      as "matchFlags%fieldsToInclude,querytype"           */
496   bitsel = fields = counter = matchsel = 0;
497   qrt = 0;
498   if (parc > 2 && parv[2] && *parv[2])
499   {
500     p = parv[2];
501     while (((ch = *(p++))) && (ch != '%') && (ch != ','))
502       switch (ch)
503       {
504         case 'o':
505         case 'O':
506           bitsel |= WHOSELECT_OPER;
507           continue;
508         case 'x':
509         case 'X':
510           bitsel |= WHOSELECT_EXTRA;
511 #ifdef WPATH
512           if (IsAnOper(sptr))
513             write_log(WPATH, "# " TIME_T_FMT " %s!%s@%s WHO %s %s\n", /* XXX DEAD */
514                 CurrentTime, sptr->name, sptr->user->username, sptr->user->host,
515                 (BadPtr(parv[3]) ? parv[1] : parv[3]), parv[2]);
516 #endif /* WPATH */
517           continue;
518         case 'n':
519         case 'N':
520           matchsel |= WHO_FIELD_NIC;
521           continue;
522         case 'u':
523         case 'U':
524           matchsel |= WHO_FIELD_UID;
525           continue;
526         case 'h':
527         case 'H':
528           matchsel |= WHO_FIELD_HOS;
529           continue;
530         case 'i':
531         case 'I':
532           matchsel |= WHO_FIELD_NIP;
533           continue;
534         case 's':
535         case 'S':
536           matchsel |= WHO_FIELD_SER;
537           continue;
538         case 'r':
539         case 'R':
540           matchsel |= WHO_FIELD_REN;
541           continue;
542       }
543     if (ch == '%')
544       while ((ch = *p++) && (ch != ','))
545       {
546         counter++;
547         switch (ch)
548         {
549           case 'c':
550           case 'C':
551             fields |= WHO_FIELD_CHA;
552             break;
553           case 'd':
554           case 'D':
555             fields |= WHO_FIELD_DIS;
556             break;
557           case 'f':
558           case 'F':
559             fields |= WHO_FIELD_FLA;
560             break;
561           case 'h':
562           case 'H':
563             fields |= WHO_FIELD_HOS;
564             break;
565           case 'i':
566           case 'I':
567             fields |= WHO_FIELD_NIP;
568             break;
569           case 'n':
570           case 'N':
571             fields |= WHO_FIELD_NIC;
572             break;
573           case 'r':
574           case 'R':
575             fields |= WHO_FIELD_REN;
576             break;
577           case 's':
578           case 'S':
579             fields |= WHO_FIELD_SER;
580             break;
581           case 't':
582           case 'T':
583             fields |= WHO_FIELD_QTY;
584             break;
585           case 'u':
586           case 'U':
587             fields |= WHO_FIELD_UID;
588             break;
589           default:
590             break;
591         }
592       };
593     if (ch)
594       qrt = p;
595   }
596
597   if (!matchsel)
598     matchsel = WHO_FIELD_DEF;
599   if (!fields)
600     counter = 7;
601
602   if (qrt && (fields & WHO_FIELD_QTY))
603   {
604     p = qrt;
605     if (!((*p > '9') || (*p < '0')))
606       p++;
607     if (!((*p > '9') || (*p < '0')))
608       p++;
609     if (!((*p > '9') || (*p < '0')))
610       p++;
611     *p = '\0';
612   }
613   else
614     qrt = 0;
615
616   /* I'd love to add also a check on the number of matches fields per time */
617   counter = (2048 / (counter + 4));
618   if (mask && (strlen(mask) > 510))
619     mask[510] = '\0';
620   move_marker();
621   commas = (mask && strchr(mask, ','));
622
623   /* First treat mask as a list of plain nicks/channels */
624   if (mask)
625   {
626     strcpy(mymask, mask);
627     for (p = 0, nick = ircd_strtok(&p, mymask, ","); nick;
628         nick = ircd_strtok(&p, 0, ","))
629     {
630       if (IsChannelName(nick) && (chptr = FindChannel(nick)))
631       {
632         isthere = (find_channel_member(sptr, chptr) != 0);
633         if (isthere || SEE_CHANNEL(sptr, chptr, bitsel))
634         {
635           struct Membership* member;
636           for (member = chptr->members; member; member = member->next_member)
637           {
638             acptr = member->user;
639             if ((bitsel & WHOSELECT_OPER) && !(IsAnOper(acptr)))
640               continue;
641             if ((acptr != sptr) && (member->status & CHFL_ZOMBIE))
642               continue;
643             if (!(isthere || (SEE_USER(sptr, acptr, bitsel))))
644               continue;
645             if (!Process(acptr))        /* This can't be moved before other checks */
646               continue;
647             if (!(isthere || (SHOW_MORE(sptr, counter))))
648               break;
649             do_who(sptr, acptr, chptr, fields, qrt);
650           }
651         }
652       }
653       else
654       {
655         if ((acptr = FindUser(nick)) &&
656             ((!(bitsel & WHOSELECT_OPER)) || IsAnOper(acptr)) &&
657             Process(acptr) && SHOW_MORE(sptr, counter))
658         {
659           do_who(sptr, acptr, 0, fields, qrt);
660         }
661       }
662     }
663   }
664
665   /* If we didn't have any comma in the mask treat it as a
666      real mask and try to match all relevant fields */
667   if (!(commas || (counter < 1)))
668   {
669     int minlen, cset;
670     static struct in_mask imask;
671     if (mask)
672     {
673       matchcomp(mymask, &minlen, &cset, mask);
674       if (matchcompIP(&imask, mask))
675         matchsel &= ~WHO_FIELD_NIP;
676       if ((minlen > NICKLEN) || !(cset & NTL_IRCNK))
677         matchsel &= ~WHO_FIELD_NIC;
678       if ((matchsel & WHO_FIELD_SER) &&
679           ((minlen > HOSTLEN) || (!(cset & NTL_IRCHN))
680           || (!markMatchexServer(mymask, minlen))))
681         matchsel &= ~WHO_FIELD_SER;
682       if ((minlen > USERLEN) || !(cset & NTL_IRCUI))
683         matchsel &= ~WHO_FIELD_UID;
684       if ((minlen > HOSTLEN) || !(cset & NTL_IRCHN))
685         matchsel &= ~WHO_FIELD_HOS;
686     }
687
688     /* First of all loop through the clients in common channels */
689     if ((!(counter < 1)) && matchsel) {
690       struct Membership* member;
691       struct Membership* chan;
692       for (chan = sptr->user->channel; chan; chan = chan->next_channel) {
693         chptr = chan->channel;
694         for (member = chptr->members; member; member = member->next_member)
695         {
696           acptr = member->user;
697           if (!(IsUser(acptr) && Process(acptr)))
698             continue;           /* Now Process() is at the beginning, if we fail
699                                    we'll never have to show this acptr in this query */
700           if ((bitsel & WHOSELECT_OPER) && !IsAnOper(acptr))
701             continue;
702           if ((mask) &&
703               ((!(matchsel & WHO_FIELD_NIC))
704               || matchexec(acptr->name, mymask, minlen))
705               && ((!(matchsel & WHO_FIELD_UID))
706               || matchexec(acptr->user->username, mymask, minlen))
707               && ((!(matchsel & WHO_FIELD_SER))
708               || (!(acptr->user->server->flags & FLAGS_MAP)))
709               && ((!(matchsel & WHO_FIELD_HOS))
710               || matchexec(acptr->user->host, mymask, minlen))
711               && ((!(matchsel & WHO_FIELD_REN))
712               || matchexec(acptr->info, mymask, minlen))
713               && ((!(matchsel & WHO_FIELD_NIP))
714               || ((((acptr->ip.s_addr & imask.mask.s_addr) !=
715               imask.bits.s_addr)) || (imask.fall
716               && matchexec(inet_ntoa(acptr->ip), mymask, minlen)))))
717             continue;
718           if (!SHOW_MORE(sptr, counter))
719             break;
720           do_who(sptr, acptr, chptr, fields, qrt);
721         }
722       }
723     }
724     /* Loop through all clients :-\, if we still have something to match to 
725        and we can show more clients */
726     if ((!(counter < 1)) && matchsel)
727       for (acptr = me.prev; acptr; acptr = acptr->prev)
728       {
729         if (!(IsUser(acptr) && Process(acptr)))
730           continue;
731         if ((bitsel & WHOSELECT_OPER) && !IsAnOper(acptr))
732           continue;
733         if (!(SEE_USER(sptr, acptr, bitsel)))
734           continue;
735         if ((mask) &&
736             ((!(matchsel & WHO_FIELD_NIC))
737             || matchexec(acptr->name, mymask, minlen))
738             && ((!(matchsel & WHO_FIELD_UID))
739             || matchexec(acptr->user->username, mymask, minlen))
740             && ((!(matchsel & WHO_FIELD_SER))
741             || (!(acptr->user->server->flags & FLAGS_MAP)))
742             && ((!(matchsel & WHO_FIELD_HOS))
743             || matchexec(acptr->user->host, mymask, minlen))
744             && ((!(matchsel & WHO_FIELD_REN))
745             || matchexec(acptr->info, mymask, minlen))
746             && ((!(matchsel & WHO_FIELD_NIP))
747             || ((((acptr->ip.s_addr & imask.mask.s_addr) != imask.bits.s_addr))
748             || (imask.fall
749             && matchexec(inet_ntoa(acptr->ip), mymask, minlen)))))
750           continue;
751         if (!SHOW_MORE(sptr, counter))
752           break;
753         do_who(sptr, acptr, 0, fields, qrt);
754       }
755   }
756
757   /* Make a clean mask suitable to be sent in the "end of" */
758   if (mask && (p = strchr(mask, ' ')))
759     *p = '\0';
760   sendto_one(sptr, rpl_str(RPL_ENDOFWHO), /* XXX DEAD */
761       me.name, parv[0], BadPtr(mask) ? "*" : mask);
762
763   /* Notify the user if we decided that his query was too long */
764   if (counter < 0)
765     sendto_one(sptr, err_str(ERR_QUERYTOOLONG), me.name, parv[0], "WHO"); /* XXX DEAD */
766
767   return 0;
768 }
769 #endif /* 0 */
770