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