"Fossies" - the Fresh Open Source Software Archive

Member "bahamut-2.1.5/src/m_rwho.c" (28 May 2020, 47347 Bytes) of package /linux/privat/bahamut-2.1.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "m_rwho.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.1.4_vs_2.1.5.

    1 /*
    2  *   m_rwho.c - Regular expression enabled WHO
    3  *   Copyright (C) 2004 Trevor Talbot and
    4  *                      the DALnet coding team
    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 
   24 #include "struct.h"
   25 #include "common.h"
   26 #include "sys.h"
   27 #include "h.h"
   28 #include "numeric.h"
   29 #include "channel.h"
   30 #include "inet.h"
   31 #include "clones.h"
   32 
   33 #include "pcre.h"
   34 
   35 extern int user_modes[];
   36 extern Link *find_channel_link(Link *, aChannel *);
   37 
   38 /* max capturing submatches to allow in all fields combined */
   39 #define MAX_SUBMATCHES  9
   40 
   41 /* for pcre_exec(), don't touch */
   42 #define NVEC        ((MAX_SUBMATCHES+1)*3)
   43 
   44 /* PCRE matched fields */
   45 #define RWHO_NICK   1
   46 #define RWHO_USER   2
   47 #define RWHO_GCOS   3
   48 #define RWHO_AWAY   4
   49 #define RWHO_COUNT  5
   50 
   51 /* other matched fields */
   52 #define RWM_AWAY    0x0001
   53 #define RWM_HOST    0x0002
   54 #define RWM_IP      0x0004
   55 #define RWM_MODES   0x0008
   56 #define RWM_SERVER  0x0010
   57 #define RWM_TS      0x0020
   58 #define RWM_STYPE   0x0040
   59 #define RWM_JOINS   0x0080
   60 #define RWM_CLONES  0x0100
   61 #define RWM_MATCHES 0x0200
   62 #define RWM_CHANNEL 0x0400
   63 #define RWM_NPROB   0x0800
   64 #define RWM_UPROB   0x1000
   65 #define RWM_GPROB   0x2000
   66 
   67 /* output options */
   68 #define RWO_NICK    0x0001
   69 #define RWO_USER    0x0002
   70 #define RWO_HOST    0x0004
   71 #define RWO_IP      0x0008
   72 #define RWO_MODES   0x0010
   73 #define RWO_FLAGS   0x0020
   74 #define RWO_SERVER  0x0040
   75 #define RWO_TS      0x0080
   76 #define RWO_STYPE   0x0100
   77 #define RWO_GCOS    0x0200
   78 #define RWO_AWAY    0x0400
   79 #define RWO_JOINS   0x0800
   80 #define RWO_CLONES  0x1000
   81 #define RWO_MATCHES 0x2000
   82 #define RWO_CHANNEL 0x4000
   83 #define RWO_PROB    0x8000
   84 #define RWO_MASKED_HOST   0x10000
   85 #define RWO_UNMASKED_HOST   0x20000
   86 
   87 /* miscellaneous flags */
   88 #define RWC_SHOWIP  0x0001  /* WHO compatibility */
   89 #define RWC_CHANNEL 0x0002  /* WHO compatibility */
   90 #define RWC_TIME    0x0004  /* show timing stats */
   91 
   92 #ifdef USER_HOSTMASKING
   93 #define RWHO_HOST(cptr) IsUmodeH(cptr)?cptr->user->mhost:cptr->user->host
   94 #else
   95 #define RWHO_HOST(cptr) cptr->user->host
   96 #endif
   97 
   98 static const char *rwho_help[] = {
   99     "RWHO <[+|-]matchflags>[/<outputflags>[:<cookie>]] <args>",
  100     "Match flags are specified like channel modes,",
  101     "'+' being a positive match and '-' being a negative one:",
  102     "  a             - user is (not) away",
  103     "  c <channel>   - user is on channel <channel> (+ only)",
  104 #ifdef THROTTLE_ENABLE
  105     "  d <clones>    - there are N or more (less) users per host",
  106     "  D <matches>   - there are N or more (less) matching users per host",
  107 #endif
  108     "  h <host>      - user's host does (not) match wildcard mask",
  109     "  i <ip>        - user's IP does (not) match CIDR <ip>",
  110     "  j <channels>  - user is in N or more (less) channels",
  111     "  m <usermodes> - user is (not) using modes <usermodes>",
  112 #ifdef RWHO_PROBABILITY
  113     "  p {N|U|G}<p>  - Nick/User/Gcos is <p> or more (less) probable",
  114     "  P <charsets>  - use custom charsets for probability search (+ only)",
  115 #endif
  116     "  s <server>    - user is (not) on server <server>",
  117     "  t <seconds>   - nick has been in use for N or more (less) seconds",
  118     "  T <type>      - user is (not) type <type> as set by services",
  119     "  C             - for compatibility with WHO",
  120     "  I             - for compatibility with WHO",
  121     "The following match flags are compiled into a single regular expression",
  122     "in the order you specify, so later flags can use backreferences to",
  123     "submatches in the flags prior:",
  124     "  A <away>      - user's away reason matches regexp pattern (implies +a)",
  125     "  g <gcos/name> - user's real name matches regexp pattern",
  126     "  n <nick>      - user's nick matches regexp pattern",
  127     "  u <username>  - user's username matches regexp pattern",
  128     "The regular expression flags do not support negative matches.",
  129     "The optional output flags cause replies to be sent using numeric 354 and",
  130     "contain only the fields associated with the flags in the order below:",
  131     "  :<cookie>     - supplied cookie (useful for scripts)",
  132     "  n             - user's nick",
  133     "  u             - user's username",
  134 #ifdef USER_HOSTMASKING
  135     "  h             - user's current host",
  136     "  H             - user's masked host",
  137     "  R             - user's real/unmasked host",
  138 #else
  139     "  h             - user's host",
  140 #endif
  141     "  i             - user's IP",
  142     "  s             - user's server",
  143     "  f             - standard WHO flags (GH*%@+)",
  144     "  c             - user's most recently joined channel",
  145     "  j             - number of joined channels",
  146 #ifdef THROTTLE_ENABLE
  147     "  d             - number of clones on user's IP",
  148     "  D             - number of matches on user's IP (see below)",
  149 #endif
  150     "  t             - nick's start-of-use timestamp",
  151     "  T             - user's type (set by services)",
  152     "  m             - user's modes",
  153 #ifdef RWHO_PROBABILITY
  154     "  p             - user's probability set",
  155 #endif
  156     "  g             - user's gcos/real name (mutually exclusive with 'a')",
  157     "  a             - user's away reason (mutually exclusive with 'g')",
  158     "Theses output flags are special:",
  159     "  L<count>      - limit to N results (no space between L and <count>)",
  160     "  C             - no results, just supply match count in RPL_ENDOFWHO",
  161 #ifdef THROTTLE_ENABLE
  162     "  D             - returns only one matching result per host (summarize)",
  163 #endif
  164     "  $             - show time taken for search",
  165     NULL
  166 };
  167 
  168 static struct {
  169     unsigned  check[2];         /* things to try match */
  170     unsigned  rplfields;        /* fields to include in the response */
  171     unsigned  misc;             /* miscellaneous flags */
  172     char     *rplcookie;        /* response cookie */
  173     int       countonly;        /* counting only, no results */
  174     int       limit;            /* max number of results */
  175     int       spat[RWHO_COUNT]; /* match string build pattern */
  176     pcre     *re;               /* regex pattern */
  177     aClient  *server;           /* server */
  178     aChannel *chptr;            /* search in channel */
  179     char     *host_pat[2];      /* wildcard host pattern */
  180     int      (*host_func[2])(); /* host match function */
  181     int       umodes[2];        /* usermodes */
  182     unsigned  stype;            /* services type */
  183     unsigned  ip_family[2]; /* CIDR family to match */
  184     int       ip_cidr_bits[2];  /* CIDR bits to match */
  185     struct
  186     {
  187     char ip[16];
  188     } ip_addr[2];       /* IP address */
  189     char     *ip_str[2];        /* IP string if CIDR is invalid */
  190     ts_val    ts[2];            /* signon timestamp */
  191     int       joined[2];        /* min/max joined chans */
  192 #ifdef THROTTLE_ENABLE
  193     int       clones[2];        /* min/max clones */
  194     int       matches[2];       /* min/max clone matches */
  195     int       thisclones;       /* number of clones on this host */
  196     int       thismatches;      /* number of matches on this host */
  197 #endif
  198 #ifdef RWHO_PROBABILITY
  199     int       nickprob[2];      /* min/max nick probability */
  200     int       userprob[2];      /* min/max username probability */
  201     int       gcosprob[2];      /* min/max real name probability */
  202 #endif
  203 } rwho_opts;
  204 
  205 static char rwhobuf[2048];
  206 static char scratch[1024];
  207 
  208 
  209 /*
  210  * Send a syntax error message.
  211  */
  212 static void rwho_synerr(aClient *sptr, char *msg)
  213 {
  214     sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name, sptr->name, "RWHO",
  215                "rwho");
  216     if (msg)
  217         sendto_one(sptr, getreply(RPL_COMMANDSYNTAX), me.name, sptr->name,msg);
  218 }
  219 
  220 /*
  221  * Build the regular expression to use for nick/user/gcos/away matching.
  222  * Returns 1 on success, 0 on failure.
  223  */
  224 static int rwho_compile(aClient *cptr, char *remap[])
  225 {
  226     const char *es;
  227     int         ei;
  228     char       *s;
  229     int         i, j;
  230     char        arg = 0;
  231 
  232     s = rwhobuf;
  233     for (i = 0; rwho_opts.spat[i]; i++)
  234         s += ircsprintf(s, "(?>(?:%s)\\x00)", remap[rwho_opts.spat[i]]);
  235 
  236     rwho_opts.re = pcre_compile(rwhobuf,
  237                                 PCRE_EXTRA|PCRE_ANCHORED|PCRE_UNGREEDY,
  238                                 &es, &ei, NULL);
  239 
  240     if (!rwho_opts.re)
  241     {
  242         rwho_synerr(cptr, NULL);
  243 
  244         /* the things we do for error messages... */
  245         for (i = 0; rwho_opts.spat[i]; i++)
  246         {
  247             rwho_opts.re = pcre_compile(remap[rwho_opts.spat[i]],
  248                                         PCRE_EXTRA|PCRE_ANCHORED|PCRE_UNGREEDY,
  249                                         &es, &ei, NULL);
  250             if (rwho_opts.re)
  251             {
  252                 free(rwho_opts.re);
  253                 continue;
  254             }
  255 
  256             if (es)
  257             {
  258                 j = 0;
  259                 s = remap[rwho_opts.spat[i]];
  260 
  261                 switch (rwho_opts.spat[i])
  262                 {
  263                     case RWHO_AWAY: arg = 'A'; break;
  264                     case RWHO_GCOS: arg = 'g'; break;
  265                     case RWHO_NICK: arg = 'n'; break;
  266                     case RWHO_USER: arg = 'u'; break;
  267                 }
  268 
  269                 while (*s)
  270                 {
  271                     if (ei == j)
  272                     {
  273                         scratch[j++] = 037;
  274                         scratch[j++] = *s++;
  275                         scratch[j++] = 037;
  276                     }
  277                     else
  278                         scratch[j++] = *s++;
  279                 }
  280                 scratch[j] = 0;
  281 
  282                 ircsprintf(rwhobuf, "Invalid flag %c expression %s", arg,
  283                            scratch);
  284                 sendto_one(cptr, getreply(RPL_COMMANDSYNTAX), me.name,
  285                            cptr->name, rwhobuf);
  286                 sendto_one(cptr, getreply(RPL_COMMANDSYNTAX), me.name,
  287                            cptr->name, es);
  288                 break;
  289             }
  290         }
  291 
  292         return 0;
  293     }
  294 
  295     pcre_fullinfo(rwho_opts.re, NULL, PCRE_INFO_CAPTURECOUNT, &ei);
  296     if (ei > MAX_SUBMATCHES)
  297     {
  298         rwho_synerr(cptr, "too many capturing submatches, use (?:)");
  299         free(rwho_opts.re);
  300         return 0;
  301     }
  302 
  303     return 1;
  304 }
  305 
  306 /*
  307  * Parse the options to the RWHO command.
  308  * Returns 1 on success, 0 on failure.
  309  */
  310 static int rwho_parseopts(aClient *sptr, int parc, char *parv[])
  311 {
  312     char       *remap[RWHO_COUNT] = {0};
  313     char       *sfl;
  314     char       *s;
  315     int         spatidx = 0;
  316     int         neg = 0;
  317     int         arg = 2;
  318     int         i;
  319     ts_val      ts;
  320     unsigned    ui;
  321 
  322     memset(&rwho_opts, 0, sizeof(rwho_opts));
  323 
  324     if (parc < 2)
  325     {
  326         sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name, sptr->name, "RWHO",
  327                    "rwho");
  328         return 0;
  329     }
  330 
  331     if (*parv[1] == '?')
  332     {
  333         const char **ptr;
  334         for (ptr = rwho_help; *ptr; ptr++)
  335             sendto_one(sptr, getreply(RPL_COMMANDSYNTAX), me.name,
  336                        parv[0], *ptr);
  337         sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, parv[0], "?","RWHO");
  338         return 0;
  339     }
  340 
  341 #ifdef RWHO_PROBABILITY
  342     probability_init();
  343 #endif
  344 
  345     /* parse match options */
  346     for (sfl = parv[1]; *sfl; sfl++)
  347     {
  348         if (*sfl == '/')
  349         {
  350             sfl++;
  351             break;
  352         }
  353 
  354         switch (*sfl)
  355         {
  356             case '+':
  357                 neg = 0;
  358                 break;
  359 
  360             case '-':
  361                 neg = 1;
  362                 break;
  363 
  364             case 'a':
  365                 if (rwho_opts.check[!neg] & RWM_AWAY)
  366                 {
  367                     rwho_synerr(sptr, "cannot use both +a and -a in match");
  368                     return 0;
  369                 }
  370                 rwho_opts.check[neg] |= RWM_AWAY;
  371                 break;
  372 
  373             case 'c':
  374                 if (!parv[arg])
  375                 {
  376                     rwho_synerr(sptr, "missing argument for match flag c");
  377                     return 0;
  378                 }
  379                 if (neg)
  380                 {
  381                     rwho_synerr(sptr, "negative match not supported for match"
  382                                 " flag c");
  383                     return 0;
  384                 }
  385                 rwho_opts.chptr = find_channel(parv[arg], NULL);
  386                 if (!rwho_opts.chptr)
  387                 {
  388                     sendto_one(sptr, getreply(ERR_NOSUCHCHANNEL), me.name,
  389                                parv[0], parv[arg]);
  390                     return 0;
  391                 }
  392                 rwho_opts.check[neg] |= RWM_CHANNEL;
  393                 arg++;
  394                 break;
  395 
  396             case 'C':
  397                 rwho_opts.misc |= RWC_CHANNEL;
  398                 break;
  399 
  400 #ifdef THROTTLE_ENABLE
  401             case 'd':
  402                 if (!parv[arg])
  403                 {
  404                     rwho_synerr(sptr, "missing argument for match flag d");
  405                     return 0;
  406                 }
  407                 i = strtol(parv[arg], &s, 0);
  408                 if (*s != 0 || i < 1)
  409                 {
  410                     rwho_synerr(sptr, "invalid number of clones for match"
  411                                 " flag d");
  412                     return 0;
  413                 }
  414                 rwho_opts.clones[neg] = i;
  415                 rwho_opts.check[neg] |= RWM_CLONES;
  416                 arg++;
  417                 break;
  418 
  419             case 'D':
  420                 if (!parv[arg])
  421                 {
  422                     rwho_synerr(sptr, "missing argument for match flag D");
  423                     return 0;
  424                 }
  425                 i = strtol(parv[arg], &s, 0);
  426                 if (*s != 0 || i < 1)
  427                 {
  428                     rwho_synerr(sptr, "invalid number of matches for match"
  429                                 " flag D");
  430                     return 0;
  431                 }
  432                 rwho_opts.matches[neg] = i;
  433                 rwho_opts.check[neg] |= RWM_MATCHES;
  434                 arg++;
  435                 break;
  436 #endif  /* THROTTLE_ENABLE */
  437 
  438             case 'h':
  439                 if (!parv[arg])
  440                 {
  441                     rwho_synerr(sptr, "missing argument for match flag h");
  442                     return 0;
  443                 }
  444                 if (strchr(parv[arg], '*') || strchr(parv[arg], '?'))
  445                     rwho_opts.host_func[neg] = match;
  446                 else
  447                     rwho_opts.host_func[neg] = mycmp;
  448                 rwho_opts.host_pat[neg] = parv[arg];
  449                 rwho_opts.check[neg] |= RWM_HOST;
  450                 arg++;
  451                 break;
  452 
  453             case 'i':
  454                 if (!parv[arg])
  455                 {
  456                     rwho_synerr(sptr, "missing argument for match flag i");
  457                     return 0;
  458                 }
  459                 if (strchr(parv[arg], '/'))
  460                 {
  461             int bits;
  462 
  463             bits = inet_parse_cidr(AF_INET, parv[arg],
  464                        &rwho_opts.ip_addr[neg],
  465                        sizeof(struct in_addr));
  466             if (bits > 0)
  467             rwho_opts.ip_family[neg] = AF_INET;
  468             else
  469             {
  470             bits = inet_parse_cidr(AF_INET6, parv[arg],
  471                            &rwho_opts.ip_addr[neg],
  472                            sizeof(struct in6_addr));
  473             if (bits > 0)
  474                 rwho_opts.ip_family[neg] = AF_INET6;
  475             }
  476             if (bits > 0)
  477             rwho_opts.ip_cidr_bits[neg] = bits;
  478             else
  479             {
  480             rwho_synerr(sptr, "invalid CIDR IP for match flag i");
  481             return 0;
  482             }
  483                 }
  484         else
  485                     rwho_opts.ip_str[neg] = parv[arg];
  486                 rwho_opts.check[neg] |= RWM_IP;
  487                 arg++;
  488                 break;
  489 
  490             case 'I':
  491                 rwho_opts.misc |= RWC_SHOWIP;
  492                 break;
  493 
  494             case 'j':
  495                 if (!parv[arg])
  496                 {
  497                     rwho_synerr(sptr, "missing argument for match flag j");
  498                     return 0;
  499                 }
  500                 i = strtol(parv[arg], &s, 0);
  501                 if (*s != 0 || i < 0)
  502                 {
  503                     rwho_synerr(sptr, "invalid number of channels for match"
  504                                 " flag j");
  505                     return 0;
  506                 }
  507                 rwho_opts.joined[neg] = i;
  508                 rwho_opts.check[neg] |= RWM_JOINS;
  509                 arg++;
  510                 break;
  511 
  512             case 'm':
  513                 if (!parv[arg])
  514                 {
  515                     rwho_synerr(sptr, "missing argument for match flag m");
  516                     return 0;
  517                 }
  518                 for (s = parv[arg]; *s; s++)
  519                 {
  520                     for (i = 1; user_modes[i]; i+=2)
  521                         if (*s == user_modes[i])
  522                         {
  523                             rwho_opts.umodes[neg] |= user_modes[i-1];
  524                             break;
  525                         }
  526                     if(!user_modes[i])
  527                     {
  528                         rwho_synerr(sptr, "Invalid argument for match flag m");
  529                         return 0;
  530                     }
  531                 }
  532                 rwho_opts.check[neg] |= RWM_MODES;
  533                 arg++;
  534                 break;
  535 
  536 #ifdef RWHO_PROBABILITY
  537             case 'p':
  538                 if (!parv[arg])
  539                 {
  540                     rwho_synerr(sptr, "missing argument for match flag p");
  541                     return 0;
  542                 }
  543                 s = parv[arg];
  544                 while (*s)
  545                 {
  546                     int *prob = NULL;
  547                     int bflag = 0;
  548 
  549                     switch (*s++)
  550                     {
  551                         case 'N':
  552                         case 'n':
  553                             prob = rwho_opts.nickprob;
  554                             bflag = RWM_NPROB;
  555                             break;
  556                         case 'U':
  557                         case 'u':
  558                             prob = rwho_opts.userprob;
  559                             bflag = RWM_UPROB;
  560                             break;
  561                         case 'G':
  562                         case 'g':
  563                             prob = rwho_opts.gcosprob;
  564                             bflag = RWM_GPROB;
  565                             break;
  566                     }
  567 
  568                     if (!prob || !IsDigit(*s))
  569                     {
  570                         rwho_synerr(sptr, "Invalid argument for match flag p");
  571                         return 0;
  572                     }
  573 
  574                     prob[neg] = strtol(s, &s, 10);
  575                     rwho_opts.check[neg] |= bflag;
  576                 }
  577                 arg++;
  578                 break;
  579 
  580             case 'P':
  581                 if (!parv[arg])
  582                 {
  583                     rwho_synerr(sptr, "missing argument for match flag P");
  584                     return 0;
  585                 }
  586                 if (neg)
  587                 {
  588                     rwho_synerr(sptr, "negative match not supported for match"
  589                                 " flag P");
  590                     return 0;
  591                 }
  592                 if (!probability_loadsets(parv[arg]))
  593                 {
  594                     rwho_synerr(sptr, "invalid argument for match flag P");
  595                     return 0;
  596                 }
  597                 arg++;
  598                 break;
  599 #endif  /* RWHO_PROBABILITY */
  600 
  601             case 's':
  602                 if (!parv[arg])
  603                 {
  604                     rwho_synerr(sptr, "missing argument for match flag s");
  605                     return 0;
  606                 }
  607                 if (rwho_opts.check[!neg] & RWM_SERVER)
  608                 {
  609                     rwho_synerr(sptr, "cannot use both +s and -s in match");
  610                     return 0;
  611                 }
  612                 rwho_opts.server = find_server(parv[arg], NULL);
  613                 if (!rwho_opts.server)
  614                 {
  615                     sendto_one(sptr, getreply(ERR_NOSUCHSERVER), me.name,
  616                                sptr->name, parv[arg]);
  617                     return 0;
  618                 }
  619                 rwho_opts.check[neg] |= RWM_SERVER;
  620                 arg++;
  621                 break;
  622 
  623             case 't':
  624                 if (!parv[arg])
  625                 {
  626                     rwho_synerr(sptr, "missing argument for match flag t");
  627                     return 0;
  628                 }
  629                 ts = strtol(parv[arg], &s, 0);
  630                 if (*s != 0 || ts <= 0)
  631                 {
  632                     rwho_synerr(sptr, "invalid number of seconds for match"
  633                                 " flag t");
  634                     return 0;
  635                 }
  636                 rwho_opts.ts[neg] = NOW - ts;
  637                 rwho_opts.check[neg] |= RWM_TS;
  638                 arg++;
  639                 break;
  640 
  641             case 'T':
  642                 if (!parv[arg])
  643                 {
  644                     rwho_synerr(sptr, "missing argument for match flag T");
  645                     return 0;
  646                 }
  647                 if (rwho_opts.check[!neg] & RWM_STYPE)
  648                 {
  649                     rwho_synerr(sptr, "cannot use both +T and -T in match");
  650                     return 0;
  651                 }
  652                 ui = strtoul(parv[arg], &s, 0);
  653                 if (*s != 0)
  654                 {
  655                     rwho_synerr(sptr, "invalid type for match flag T");
  656                     return 0;
  657                 }
  658                 rwho_opts.stype = ui;
  659                 rwho_opts.check[neg] |= RWM_STYPE;
  660                 arg++;
  661                 break;
  662 
  663             case 'A':
  664                 if (!parv[arg])
  665                 {
  666                     rwho_synerr(sptr, "missing argument for match flag A");
  667                     return 0;
  668                 }
  669                 if (remap[RWHO_AWAY])
  670                 {
  671                     rwho_synerr(sptr, "flags may not be used more than once");
  672                     return 0;
  673                 }
  674                 if (neg)
  675                 {
  676                     rwho_synerr(sptr, "negative match not supported for match"
  677                                 " flag A");
  678                     return 0;
  679                 }
  680                 if (rwho_opts.check[!neg] & RWM_AWAY)
  681                 {
  682                     rwho_synerr(sptr, "cannot use both +A and -a in match");
  683                     return 0;
  684                 }
  685                 remap[RWHO_AWAY] = parv[arg];
  686                 rwho_opts.spat[spatidx++] = RWHO_AWAY;
  687                 rwho_opts.check[neg] |= RWM_AWAY;    /* implicit +a */
  688                 arg++;
  689                 break;
  690 
  691             case 'g':
  692                 if (!parv[arg])
  693                 {
  694                     rwho_synerr(sptr, "missing argument for match flag g");
  695                     return 0;
  696                 }
  697                 if (remap[RWHO_GCOS])
  698                 {
  699                     rwho_synerr(sptr, "flags may not be used more than once");
  700                     return 0;
  701                 }
  702                 if (neg)
  703                 {
  704                     rwho_synerr(sptr, "negative match not supported for match"
  705                                 " flag g");
  706                     return 0;
  707                 }
  708                 remap[RWHO_GCOS] = parv[arg];
  709                 rwho_opts.spat[spatidx++] = RWHO_GCOS;
  710                 arg++;
  711                 break;
  712 
  713             case 'n':
  714                 if (!parv[arg])
  715                 {
  716                     rwho_synerr(sptr, "missing argument for match flag n");
  717                     return 0;
  718                 }
  719                 if (remap[RWHO_NICK])
  720                 {
  721                     rwho_synerr(sptr, "flags may not be used more than once");
  722                     return 0;
  723                 }
  724                 if (neg)
  725                 {
  726                     rwho_synerr(sptr, "negative match not supported for match"
  727                                 " flag n");
  728                     return 0;
  729                 }
  730                 remap[RWHO_NICK] = parv[arg];
  731                 rwho_opts.spat[spatidx++] = RWHO_NICK;
  732                 arg++;
  733                 break;
  734 
  735             case 'u':
  736                 if (!parv[arg])
  737                 {
  738                     rwho_synerr(sptr, "missing argument for match flag u");
  739                     return 0;
  740                 }
  741                 if (remap[RWHO_USER])
  742                 {
  743                     rwho_synerr(sptr, "flags may not be used more than once");
  744                     return 0;
  745                 }
  746                 if (neg)
  747                 {
  748                     rwho_synerr(sptr, "negative match not supported for match"
  749                                 " flag u");
  750                     return 0;
  751                 }
  752                 remap[RWHO_USER] = parv[arg];
  753                 rwho_opts.spat[spatidx++] = RWHO_USER;
  754                 arg++;
  755                 break;
  756                 
  757             default:
  758                 ircsprintf(scratch, "unknown match flag %c", *sfl);
  759                 rwho_synerr(sptr, scratch);
  760                 return 0;
  761         }
  762     }
  763 
  764     /* parse output options */
  765     while (*sfl)
  766     {
  767         if (*sfl == ':')
  768         {
  769             if (!*++sfl)
  770             {
  771                 rwho_synerr(sptr, NULL);
  772                 return 0;
  773             }
  774             rwho_opts.rplcookie = sfl;
  775             break;
  776         }
  777 
  778         switch (*sfl)
  779         {
  780             case 'n': rwho_opts.rplfields |= RWO_NICK; sfl++; break;
  781             case 'u': rwho_opts.rplfields |= RWO_USER; sfl++; break;
  782             case 'h': rwho_opts.rplfields |= RWO_HOST; sfl++; break;
  783 #ifdef USER_HOSTMASKING
  784             case 'H': rwho_opts.rplfields |= RWO_MASKED_HOST; sfl++; break;
  785             case 'R': rwho_opts.rplfields |= RWO_UNMASKED_HOST; sfl++; break;
  786 #endif
  787             case 'i': rwho_opts.rplfields |= RWO_IP; sfl++; break;
  788             case 's': rwho_opts.rplfields |= RWO_SERVER; sfl++; break;
  789             case 'f': rwho_opts.rplfields |= RWO_FLAGS; sfl++; break;
  790             case 'c': rwho_opts.rplfields |= RWO_CHANNEL; sfl++; break;
  791             case 'j': rwho_opts.rplfields |= RWO_JOINS; sfl++; break;
  792 #ifdef THROTTLE_ENABLE
  793             case 'd': rwho_opts.rplfields |= RWO_CLONES; sfl++; break;
  794             case 'D': rwho_opts.rplfields |= RWO_MATCHES; sfl++; break;
  795 #endif
  796             case 't': rwho_opts.rplfields |= RWO_TS; sfl++; break;
  797             case 'T': rwho_opts.rplfields |= RWO_STYPE; sfl++; break;
  798             case 'm': rwho_opts.rplfields |= RWO_MODES; sfl++; break;
  799 #ifdef RWHO_PROBABILITY
  800             case 'p': rwho_opts.rplfields |= RWO_PROB; sfl++; break;
  801 #endif
  802             case 'g': rwho_opts.rplfields |= RWO_GCOS; sfl++; break;
  803             case 'a': rwho_opts.rplfields |= RWO_AWAY; sfl++; break;
  804 
  805             case 'C': rwho_opts.countonly = 1; sfl++; break;
  806             case '$': rwho_opts.misc |= RWC_TIME; sfl++; break;
  807 
  808             case 'L':
  809                 rwho_opts.limit = strtol(sfl+1, &sfl, 10);
  810                 if (rwho_opts.limit < 1)
  811                 {
  812                     rwho_synerr(sptr, "invalid limit for output flag L");
  813                     return 0;
  814                 }
  815                 break;
  816 
  817             default:
  818                 ircsprintf(scratch, "unknown output flag %c", *sfl);
  819                 rwho_synerr(sptr, scratch);
  820                 return 0;
  821         }
  822     }
  823 
  824     if(parc > arg)
  825     {
  826         rwho_synerr(sptr, "Too many arguments");
  827         return 0;
  828     }
  829 
  830 
  831     if ((rwho_opts.rplfields & (RWO_GCOS|RWO_AWAY)) == (RWO_GCOS|RWO_AWAY))
  832     {
  833         rwho_synerr(sptr, "output flags g and a may not be used together");
  834         return 0;
  835     }
  836 
  837 #ifdef THROTTLE_ENABLE
  838     if ((rwho_opts.check[0] & rwho_opts.check[1] & RWM_CLONES)
  839         && (rwho_opts.clones[0] > rwho_opts.clones[1]))
  840     {
  841         rwho_synerr(sptr, "values for match flags +d and -d will never match");
  842         return 0;
  843     }
  844 
  845     if ((rwho_opts.check[0] & rwho_opts.check[1] & RWM_MATCHES)
  846         && (rwho_opts.matches[0] > rwho_opts.matches[1]))
  847     {
  848         rwho_synerr(sptr, "values for match flags +D and -D will never match");
  849         return 0;
  850     }
  851 #endif
  852 
  853     if ((rwho_opts.check[0] & rwho_opts.check[1] & RWM_JOINS)
  854         && (rwho_opts.joined[0] > rwho_opts.joined[1]))
  855     {
  856         rwho_synerr(sptr, "values for match flags +j and -j will never match");
  857         return 0;
  858     }
  859 
  860     if ((rwho_opts.check[0] & rwho_opts.check[1] & RWM_TS)
  861         && (rwho_opts.ts[0] < rwho_opts.ts[1]))
  862     {
  863         rwho_synerr(sptr, "values for match flags +t and -t will never match");
  864         return 0;
  865     }
  866 
  867 #ifdef RWHO_PROBABILITY
  868     if ((rwho_opts.check[0] & rwho_opts.check[1] & RWM_NPROB)
  869         && (rwho_opts.nickprob[0] > rwho_opts.nickprob[1]))
  870     {
  871         rwho_synerr(sptr, "values for match flags +p and -p will never match");
  872         return 0;
  873     }
  874 
  875     if ((rwho_opts.check[0] & rwho_opts.check[1] & RWM_UPROB)
  876         && (rwho_opts.userprob[0] > rwho_opts.userprob[1]))
  877     {
  878         rwho_synerr(sptr, "values for match flags +p and -p will never match");
  879         return 0;
  880     }
  881 
  882     if ((rwho_opts.check[0] & rwho_opts.check[1] & RWM_GPROB)
  883         && (rwho_opts.gcosprob[0] > rwho_opts.gcosprob[1]))
  884     {
  885         rwho_synerr(sptr, "values for match flags +p and -p will never match");
  886         return 0;
  887     }
  888 
  889     /* ugly, but this is an expensive calculation, do it only if necessary */
  890     if ( ((rwho_opts.check[0] | rwho_opts.check[1])
  891           & (RWM_NPROB|RWM_UPROB|RWM_GPROB))
  892          || (rwho_opts.rplfields & RWO_PROB) )
  893         probability_fini();
  894 #endif  /* RWHO_PROBABILITY */
  895 
  896     if (spatidx && !rwho_compile(sptr, remap))
  897         return 0;
  898 
  899     return 1;
  900 }
  901 
  902 /*
  903  * See if a client matches the search parameters.
  904  * Returns 1 on match, 0 on no match.
  905  * Fills in failcode and failclient upon unexpected PCRE error.
  906  */
  907 static int rwho_match(aClient *cptr, int *failcode, aClient **failclient)
  908 {
  909     char *s;
  910     char *b;
  911     int   i;
  912     int   ovec[NVEC];
  913 
  914     if ((rwho_opts.check[0] & RWM_SERVER) &&
  915         (cptr->uplink != rwho_opts.server))
  916         return 0;
  917     else if ((rwho_opts.check[1] & RWM_SERVER) &&
  918         (cptr->uplink == rwho_opts.server))
  919         return 0;
  920 
  921     if ((rwho_opts.check[0] & RWM_TS) && (cptr->tsinfo > rwho_opts.ts[0]))
  922         return 0;
  923 
  924     if ((rwho_opts.check[1] & RWM_TS) && (cptr->tsinfo < rwho_opts.ts[1]))
  925         return 0;
  926 
  927     if ((rwho_opts.check[0] & RWM_AWAY) && !cptr->user->away)
  928         return 0;
  929     else if ((rwho_opts.check[1] & RWM_AWAY) && cptr->user->away)
  930         return 0;
  931 
  932     if ((rwho_opts.check[0] & RWM_STYPE) &&
  933         (cptr->user->servicetype != rwho_opts.stype))
  934         return 0;
  935     else if ((rwho_opts.check[1] & RWM_STYPE) &&
  936         (cptr->user->servicetype == rwho_opts.stype))
  937         return 0;
  938 
  939     if ((rwho_opts.check[0] & RWM_JOINS) &&
  940         (cptr->user->joined < rwho_opts.joined[0]))
  941         return 0;
  942 
  943     if ((rwho_opts.check[1] & RWM_JOINS) &&
  944         (cptr->user->joined > rwho_opts.joined[1]))
  945         return 0;
  946 
  947     if ((rwho_opts.check[0] & RWM_MODES) &&
  948         ((cptr->umode & rwho_opts.umodes[0]) != rwho_opts.umodes[0]))
  949         return 0;
  950 
  951     if ((rwho_opts.check[1] & RWM_MODES) &&
  952         (cptr->umode & rwho_opts.umodes[1]))
  953         return 0;
  954 
  955     if ((rwho_opts.check[0] & RWM_CHANNEL) && !IsMember(cptr, rwho_opts.chptr))
  956         return 0;
  957 
  958     if (rwho_opts.check[0] & RWM_IP)
  959     {
  960     if (rwho_opts.ip_str[0])
  961         {
  962             if (match(rwho_opts.ip_str[0], cptr->hostip))
  963                 return 0;
  964         }
  965         else if (cptr->ip_family == rwho_opts.ip_family[0])
  966     {
  967         if (bitncmp(&cptr->ip, &rwho_opts.ip_addr[0],
  968             rwho_opts.ip_cidr_bits[0]) != 0)
  969         return 0;
  970     }
  971     else
  972         return 0;
  973     }
  974 
  975     if (rwho_opts.check[1] & RWM_IP)
  976     {
  977     if (rwho_opts.ip_str[1])
  978         {
  979             if (!match(rwho_opts.ip_str[1], cptr->hostip))
  980                 return 0;
  981         }
  982         else if (cptr->ip_family == rwho_opts.ip_family[1])
  983     {
  984         if (bitncmp(&cptr->ip, &rwho_opts.ip_addr[1],
  985             rwho_opts.ip_cidr_bits[1]) == 0)
  986         return 0;
  987     }
  988     else
  989         return 0;
  990     }
  991 
  992     if ((rwho_opts.check[0] & RWM_HOST) &&
  993         rwho_opts.host_func[0](rwho_opts.host_pat[0], cptr->user->host) && rwho_opts.host_func[0](rwho_opts.host_pat[0], cptr->user->mhost))
  994         return 0;
  995 
  996     if ((rwho_opts.check[1] & RWM_HOST) &&
  997         !rwho_opts.host_func[1](rwho_opts.host_pat[1], cptr->user->host) && !rwho_opts.host_func[1](rwho_opts.host_pat[1], cptr->user->mhost))
  998         return 0;
  999 
 1000 #ifdef RWHO_PROBABILITY
 1001     if ((rwho_opts.check[0] | rwho_opts.check[1]) &
 1002         (RWM_NPROB|RWM_UPROB|RWM_GPROB))
 1003     {
 1004         int np, up, gp;
 1005         get_probabilities(cptr, &np, &up, &gp);
 1006         if ((rwho_opts.check[0] & RWM_NPROB) && np < rwho_opts.nickprob[0])
 1007             return 0;
 1008         if ((rwho_opts.check[1] & RWM_NPROB) && np > rwho_opts.nickprob[1])
 1009             return 0;
 1010         if ((rwho_opts.check[0] & RWM_UPROB) && up < rwho_opts.userprob[0])
 1011             return 0;
 1012         if ((rwho_opts.check[1] & RWM_UPROB) && up > rwho_opts.userprob[1])
 1013             return 0;
 1014         if ((rwho_opts.check[0] & RWM_GPROB) && gp < rwho_opts.gcosprob[0])
 1015             return 0;
 1016         if ((rwho_opts.check[1] & RWM_GPROB) && gp > rwho_opts.gcosprob[1])
 1017             return 0;
 1018     }
 1019 #endif
 1020 
 1021     if (rwho_opts.re)
 1022     {
 1023         s = scratch;
 1024         for (i = 0; rwho_opts.spat[i]; i++)
 1025         {
 1026             switch (rwho_opts.spat[i])
 1027             {
 1028                 case RWHO_NICK:
 1029                     b = cptr->name;
 1030                     while ((*s++ = *b++));
 1031                     /* note: deliberately using zero terminator */
 1032                     break;
 1033 
 1034                 case RWHO_USER:
 1035                     b = cptr->user->username;
 1036                     while ((*s++ = *b++));
 1037                     break;
 1038 
 1039                 case RWHO_GCOS:
 1040                     b = cptr->info;
 1041                     while ((*s++ = *b++));
 1042                     break;
 1043 
 1044                 /* will core if RWM_AWAY wasn't implicitly set */
 1045                 case RWHO_AWAY:
 1046                     b = cptr->user->away;
 1047                     while ((*s++ = *b++));
 1048                     break;
 1049             }
 1050         }
 1051 
 1052         i = pcre_exec(rwho_opts.re, NULL, scratch, s - scratch, 0, 0, ovec,
 1053                       NVEC);
 1054 
 1055         if (i < 0)
 1056         {
 1057             if (i == PCRE_ERROR_NOMATCH)
 1058                 return 0;
 1059 
 1060             *failcode = i;
 1061             *failclient = cptr;
 1062             return 0;
 1063         }
 1064     }
 1065 
 1066     return 1;
 1067 }
 1068 
 1069 /*
 1070  * Prepare rwhobuf for response text.
 1071  * Returns a pointer to space for rwho_reply().
 1072  */
 1073 static char *rwho_prepbuf(aClient *cptr)
 1074 {
 1075     char *s = rwhobuf;
 1076 
 1077     if (!rwho_opts.rplfields)
 1078         return s;
 1079 
 1080     s += ircsprintf(s, getreply(RPL_RWHOREPLY), me.name, cptr->name);
 1081 
 1082     if (rwho_opts.rplcookie)
 1083         s += ircsprintf(s, " %s", rwho_opts.rplcookie);
 1084 
 1085     return s;
 1086 }
 1087 
 1088 /*
 1089  * Build response text in supplied buffer.
 1090  */
 1091 static void rwho_reply(aClient *cptr, aClient *ac, char *buf, chanMember *cm)
 1092 {
 1093     char     *src;
 1094     char     *dst;
 1095     aChannel *chptr = NULL;
 1096 
 1097     dst = buf;
 1098 
 1099     if (ac->user->channel)
 1100         chptr = ac->user->channel->value.chptr;
 1101 
 1102     /* use standard RPL_WHOREPLY if no output flags */
 1103     if (!rwho_opts.rplfields)
 1104     {
 1105         char status[5];
 1106         char chname[CHANNELLEN+2] = "*";
 1107 
 1108         if (!cm && (rwho_opts.misc & RWC_CHANNEL) && chptr)
 1109         {
 1110             for (cm = chptr->members; cm; cm = cm->next)
 1111                 if (cm->cptr == ac)
 1112                     break;
 1113         }
 1114 
 1115         dst = status;
 1116         if (ac->user->away)
 1117             *dst++ = 'G';
 1118         else
 1119             *dst++ = 'H';
 1120         if (IsAnOper(ac))
 1121             *dst++ = '*';
 1122         else if (IsInvisible(ac))
 1123             *dst++ = '%';
 1124         if (cm)
 1125         {
 1126             if (cm->flags & CHFL_CHANOP)
 1127                 *dst++ = '@';
 1128             else if (cm->flags & CHFL_VOICE)
 1129                 *dst++ = '+';
 1130         }
 1131         *dst = 0;
 1132 
 1133         if (!rwho_opts.chptr && (rwho_opts.misc & RWC_CHANNEL) && chptr)
 1134         {
 1135             dst = chname;
 1136             if (!PubChannel(chptr))
 1137                 *dst++ = '%';
 1138             if (PubChannel(chptr) || IsAdmin(cptr))
 1139                 strcpy(dst, chptr->chname);
 1140         }
 1141 
 1142         if (rwho_opts.misc & RWC_SHOWIP)
 1143             src = ac->hostip;
 1144         else
 1145             src = RWHO_HOST(ac);
 1146 
 1147         ircsprintf(buf, getreply(RPL_WHOREPLY), me.name, cptr->name,
 1148                    rwho_opts.chptr ? rwho_opts.chptr->chname : chname,
 1149                    ac->user->username, src, ac->user->server,
 1150                    ac->name, status, ac->hopcount, ac->info);
 1151         return;
 1152     }
 1153 
 1154     if (rwho_opts.rplfields & RWO_NICK)
 1155     {
 1156         src = ac->name;
 1157         *dst++ = ' ';
 1158         while (*src)
 1159             *dst++ = *src++;
 1160     }
 1161 
 1162     if (rwho_opts.rplfields & RWO_USER)
 1163     {
 1164         src = ac->user->username;
 1165         *dst++ = ' ';
 1166         while (*src)
 1167             *dst++ = *src++;
 1168     }
 1169 
 1170     if (rwho_opts.rplfields & RWO_HOST)
 1171     {
 1172         src = RWHO_HOST(ac);
 1173         *dst++ = ' ';
 1174         while (*src)
 1175             *dst++ = *src++;
 1176     }
 1177 
 1178 #ifdef USER_HOSTMASKING
 1179     if (rwho_opts.rplfields & RWO_MASKED_HOST)
 1180     {
 1181         src = ac->user->mhost;
 1182         *dst++ = ' ';
 1183         while (*src)
 1184             *dst++ = *src++;
 1185     }
 1186     if (rwho_opts.rplfields & RWO_UNMASKED_HOST)
 1187     {
 1188         src = ac->user->host;
 1189         *dst++ = ' ';
 1190         while (*src)
 1191             *dst++ = *src++;
 1192     }
 1193 #endif
 1194 
 1195     if (rwho_opts.rplfields & RWO_IP)
 1196     {
 1197         src = ac->hostip;
 1198         *dst++ = ' ';
 1199         while (*src)
 1200             *dst++ = *src++;
 1201     }
 1202 
 1203     if (rwho_opts.rplfields & RWO_SERVER)
 1204     {
 1205         src = ac->user->server;
 1206         *dst++ = ' ';
 1207         while (*src)
 1208             *dst++ = *src++;
 1209     }
 1210 
 1211     if (rwho_opts.rplfields & RWO_FLAGS)
 1212     {
 1213         *dst++ = ' ';
 1214         if (ac->user->away)
 1215             *dst++ = 'G';
 1216         else
 1217             *dst++ = 'H';
 1218         if (IsAnOper(ac))
 1219             *dst++ = '*';
 1220         if (IsInvisible(ac))
 1221             *dst++ = '%';
 1222 
 1223         if (!cm && (rwho_opts.rplfields & RWO_CHANNEL) && chptr)
 1224         {
 1225             for (cm = chptr->members; cm; cm = cm->next)
 1226                 if (cm->cptr == ac)
 1227                     break;
 1228         }
 1229 
 1230         if (cm)
 1231         {
 1232             if (cm->flags & CHFL_CHANOP)
 1233                 *dst++ = '@';
 1234             if (cm->flags & CHFL_VOICE)
 1235                 *dst++ = '+';
 1236         }
 1237     }
 1238 
 1239     if (rwho_opts.rplfields & RWO_CHANNEL)
 1240     {
 1241         *dst++ = ' ';
 1242 
 1243         if (!chptr)
 1244             *dst++ = '*';
 1245         else
 1246         {
 1247             if (!PubChannel(chptr))
 1248                 *dst++ = '%';
 1249             if (PubChannel(chptr) || IsAdmin(cptr))
 1250             {
 1251                 src = chptr->chname;
 1252                 while (*src)
 1253                     *dst++ = *src++;
 1254             }
 1255         }
 1256     }
 1257 
 1258     if (rwho_opts.rplfields & RWO_JOINS)
 1259         dst += ircsprintf(dst, " %d", ac->user->joined);
 1260 
 1261 #ifdef THROTTLE_ENABLE
 1262     if (rwho_opts.rplfields & RWO_CLONES)
 1263         dst += ircsprintf(dst, " %d", rwho_opts.thisclones);
 1264 
 1265     if (rwho_opts.rplfields & RWO_MATCHES)
 1266         dst += ircsprintf(dst, " %d", rwho_opts.thismatches);
 1267 #endif
 1268 
 1269     if (rwho_opts.rplfields & RWO_TS)
 1270         dst += ircsprintf(dst, " %ld", (long)ac->tsinfo);
 1271 
 1272     if (rwho_opts.rplfields & RWO_STYPE)
 1273         dst += ircsprintf(dst, " %d", ac->user->servicetype);
 1274 
 1275     if (rwho_opts.rplfields & RWO_MODES)
 1276     {
 1277         int i;
 1278 
 1279         *dst++ = ' ';
 1280         *dst++ = '+';
 1281         for (i = 0; user_modes[i]; i += 2)
 1282         {
 1283             if (ac->umode & user_modes[i])
 1284                 *dst++ = user_modes[i+1];
 1285         }
 1286     }
 1287 
 1288 #ifdef RWHO_PROBABILITY
 1289     if (rwho_opts.rplfields & RWO_PROB)
 1290     {
 1291         int np, up, gp;
 1292         get_probabilities(ac, &np, &up, &gp);
 1293         dst += ircsprintf(dst, " N%dU%dG%d", np, up, gp);
 1294     }
 1295 #endif
 1296 
 1297     if (rwho_opts.rplfields & RWO_GCOS)
 1298     {
 1299         src = ac->info;
 1300         *dst++ = ' ';
 1301         *dst++ = ':';
 1302         while (*src)
 1303             *dst++ = *src++;
 1304     }
 1305     else if (rwho_opts.rplfields & RWO_AWAY)
 1306     {
 1307         src = ac->user->away;
 1308         *dst++ = ' ';
 1309         *dst++ = ':';
 1310         if (src)
 1311             while (*src)
 1312                 *dst++ = *src++;
 1313     }
 1314 
 1315     *dst = 0;
 1316 }
 1317 
 1318 /*
 1319  * m_rwho - flexible client search with regular expression support
 1320  * parv[0] - sender
 1321  * parv[1] - flags
 1322  * parv[2] - arguments
 1323  */
 1324 int m_rwho(aClient *cptr, aClient *sptr, int parc, char *parv[])
 1325 {
 1326     chanMember *cm;
 1327     aClient    *ac;
 1328     aClient    *failclient = NULL;
 1329     int         failcode = 0;
 1330     int         results = 0;
 1331     int         left;
 1332     char       *fill;
 1333     clock_t     cbegin;
 1334     clock_t     cend;
 1335 
 1336     if (!IsAnOper(sptr))
 1337     {
 1338         sendto_one(sptr, getreply(ERR_NOPRIVILEGES), me.name, parv[0]);
 1339         return 0;
 1340     }
 1341 
 1342     cbegin = clock();
 1343 
 1344     if (!rwho_parseopts(sptr, parc, parv))
 1345         return 0;
 1346 
 1347     left = rwho_opts.limit ? rwho_opts.limit : INT_MAX;
 1348 
 1349     fill = rwho_prepbuf(sptr);
 1350 
 1351     if (rwho_opts.chptr && !IsAdmin(sptr) && !ShowChannel(sptr, rwho_opts.chptr))
 1352         rwho_opts.countonly = 1;
 1353 
 1354 #ifdef THROTTLE_ENABLE
 1355     if (((rwho_opts.check[0] | rwho_opts.check[1]) & (RWM_CLONES|RWM_MATCHES))
 1356         || (rwho_opts.rplfields & (RWO_CLONES|RWO_MATCHES)))
 1357     {
 1358         CloneEnt *ce;
 1359         aClient *fm;
 1360 
 1361         for (ce = clones_list; ce; ce = ce->next)
 1362         {
 1363             if (!ce->clients)
 1364                 continue;
 1365 
 1366             if ((rwho_opts.check[0] & RWM_CLONES) &&
 1367                 (ce->gcount < rwho_opts.clones[0]))
 1368                 continue;
 1369 
 1370             if ((rwho_opts.check[1] & RWM_CLONES) &&
 1371                 (ce->gcount > rwho_opts.clones[1]))
 1372                 continue;
 1373 
 1374             fm = NULL;
 1375             rwho_opts.thismatches = 0;
 1376             rwho_opts.thisclones = ce->gcount;
 1377 
 1378             /* if using match flag D or summarizing, we need the match count */
 1379             if (((rwho_opts.check[0] | rwho_opts.check[1]) & RWM_MATCHES)
 1380                 || (rwho_opts.rplfields & RWO_MATCHES))
 1381             {
 1382                 for (ac = ce->clients; ac; ac = ac->clone.next)
 1383                 {
 1384                     if (!rwho_match(ac, &failcode, &failclient))
 1385                         continue;
 1386 
 1387                     if (!fm)
 1388                         fm = ac;
 1389 
 1390                     rwho_opts.thismatches++;
 1391                 }
 1392 
 1393                 /* we know no matches, so no need to process further */
 1394                 if (!rwho_opts.thismatches)
 1395                     continue;
 1396 
 1397                 if ((rwho_opts.check[0] & RWM_MATCHES) &&
 1398                     (rwho_opts.thismatches < rwho_opts.matches[0]))
 1399                     continue;
 1400 
 1401                 if ((rwho_opts.check[1] & RWM_MATCHES) &&
 1402                     (rwho_opts.thismatches > rwho_opts.matches[1]))
 1403                     continue;
 1404             }
 1405 
 1406             /* if summarizing, we cached from the sweep above */
 1407             if (rwho_opts.rplfields & RWO_MATCHES)
 1408             {
 1409                 if (!left)
 1410                 {
 1411                     sendto_one(sptr, getreply(ERR_WHOLIMEXCEED), me.name,
 1412                                parv[0], rwho_opts.limit, "RWHO");
 1413                     break;
 1414                 }
 1415 
 1416                 if (!rwho_opts.countonly)
 1417                 {
 1418                     rwho_reply(sptr, fm, fill, NULL);
 1419                     sendto_one(sptr, "%s", rwhobuf);
 1420                 }
 1421 
 1422                 results++;
 1423                 left--;
 1424                 continue;
 1425             }
 1426 
 1427             /* not summarizing, so send each match */
 1428             for (ac = ce->clients; ac; ac = ac->clone.next)
 1429             {
 1430                 if (!rwho_match(ac, &failcode, &failclient))
 1431                     continue;
 1432 
 1433                 if (!left)
 1434                     break;
 1435 
 1436                 if (!rwho_opts.countonly)
 1437                 {
 1438                     rwho_reply(sptr, ac, fill, NULL);
 1439                     sendto_one(sptr, "%s", rwhobuf);
 1440                 }
 1441 
 1442                 results++;
 1443                 left--;
 1444             }
 1445 
 1446             /* This may be inaccurate.  If the loop above finished without
 1447                hitting the limit, this reply is too early -- it suggests there
 1448                are more matches when there may not be.  But it's the easiest
 1449                way to handle this case at present. */
 1450             if (!left)
 1451             {
 1452                 sendto_one(sptr, getreply(ERR_WHOLIMEXCEED), me.name, parv[0],
 1453                            rwho_opts.limit, "RWHO");
 1454                 break;
 1455             }
 1456         }
 1457     }
 1458     else
 1459 #endif  /* THROTTLE_ENABLE */
 1460     if (rwho_opts.chptr)
 1461     {
 1462         rwho_opts.check[0] &= ~RWM_CHANNEL;
 1463 
 1464         for (cm = rwho_opts.chptr->members; cm; cm = cm->next)
 1465         {
 1466             ac = cm->cptr;
 1467 
 1468             if (!rwho_match(ac, &failcode, &failclient))
 1469                 continue;
 1470 
 1471             if (!left)
 1472             {
 1473                 sendto_one(sptr, getreply(ERR_WHOLIMEXCEED), me.name, parv[0],
 1474                            rwho_opts.limit, "RWHO");
 1475                 break;
 1476             }
 1477 
 1478             if (!rwho_opts.countonly)
 1479             {
 1480                 rwho_reply(sptr, ac, fill, cm);
 1481                 sendto_one(sptr, "%s", rwhobuf);
 1482             }
 1483 
 1484             results++;
 1485             left--;
 1486         }
 1487     }
 1488     else
 1489     {
 1490         for (ac = client; ac; ac = ac->next)
 1491         {
 1492             if (!IsClient(ac))
 1493                 continue;
 1494 
 1495             if (!rwho_match(ac, &failcode, &failclient))
 1496                 continue;
 1497 
 1498             if (!left)
 1499             {
 1500                 sendto_one(sptr, getreply(ERR_WHOLIMEXCEED), me.name, parv[0],
 1501                            rwho_opts.limit, "RWHO");
 1502                 break;
 1503             }
 1504 
 1505             if (!rwho_opts.countonly)
 1506             {
 1507                 rwho_reply(sptr, ac, fill, NULL);
 1508                 sendto_one(sptr, "%s", rwhobuf);
 1509             }
 1510 
 1511             results++;
 1512             left--;
 1513         }
 1514     }
 1515 
 1516     cend = clock();
 1517     if (rwho_opts.misc & RWC_TIME)
 1518     {
 1519         ircsprintf(rwhobuf, "Search completed in %.03fs.",
 1520                    ((double)(cend - cbegin)) / CLOCKS_PER_SEC);
 1521         sendto_one(sptr, getreply(RPL_COMMANDSYNTAX), me.name, sptr->name,
 1522                    rwhobuf);
 1523     }
 1524     
 1525     if (rwho_opts.rplcookie)
 1526         ircsprintf(rwhobuf, "%d:%s", results, rwho_opts.rplcookie);
 1527     else
 1528         ircsprintf(rwhobuf, "%d", results);
 1529     sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, parv[0], rwhobuf,"RWHO");
 1530 
 1531     if (failcode)
 1532     {
 1533         if (failcode == PCRE_ERROR_MATCHLIMIT)
 1534         {
 1535             sendto_one(sptr, ":%s NOTICE %s :RWHO: Regex match pattern is too "
 1536                        "recursive, so some matches failed prematurely.  Use a "
 1537                        "more specific pattern.", me.name, parv[0]);
 1538         }
 1539         else
 1540         {
 1541             sendto_one(sptr, ":%s NOTICE %s :RWHO: Internal error %d during "
 1542                        "match, notify coders!", me.name, parv[0], failcode);
 1543             sendto_one(sptr, ":%s NOTICE %s :RWHO: Match target was: %s %s "
 1544                        "[%s] [%s]", me.name, parv[0], failclient->name,
 1545                        failclient->user->username, failclient->info,
 1546                        failclient->user->away ? failclient->user->away : "");
 1547         }
 1548     }
 1549 
 1550     free(rwho_opts.re);
 1551 
 1552     return 0;
 1553 }
 1554