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