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