fixed ssl.c bug when ssl backend returns IO_BLOCKED but IO engine doesn't get informe...
[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: m_who.c 1829 2007-08-14 03:54:48Z entrope $
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 #include "channel.h"
85 #include "client.h"
86 #include "hash.h"
87 #include "ircd.h"
88 #include "ircd_chattr.h"
89 #include "ircd_features.h"
90 #include "ircd_log.h"
91 #include "ircd_reply.h"
92 #include "ircd_string.h"
93 #include "match.h"
94 #include "numeric.h"
95 #include "numnicks.h"
96 #include "send.h"
97 #include "whocmds.h"
98
99 /* #include <assert.h> -- Now using assert in ircd_log.h */
100 #include <string.h>
101
102
103 /*
104  * A little spin-marking utility to tell us which clients we have already
105  * processed and which not
106  */
107 static int who_marker = 0;
108 static void move_marker(void)
109 {
110   if (!++who_marker)
111   {
112     struct Client *cptr = GlobalClientList;
113     while (cptr)
114     {
115       cli_marker(cptr) = 0;
116       cptr = cli_next(cptr);
117     }
118     who_marker++;
119   }
120 }
121
122 #define CheckMark(x, y) ((x == y) ? 0 : (x = y))
123 #define Process(cptr) CheckMark(cli_marker(cptr), who_marker)
124
125 /*
126  * m_who - generic message handler
127  *
128  *  parv[0] = sender prefix
129  *  parv[1] = nickname mask list
130  *  parv[2] = additional selection flag, only 'o' for now.
131  *            and %flags to specify what fields to output
132  *            plus a ,querytype if the t flag is specified
133  *            so the final thing will be like o%tnchu,777
134  *  parv[3] = _optional_ parameter that overrides parv[1]
135  *            This can be used as "/quote who foo % :The Black Hacker
136  *            to find me, parv[3] _can_ contain spaces !.
137  */
138 int m_who(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
139 {
140   char *mask;           /* The mask we are looking for              */
141   char ch;                      /* Scratch char register                    */
142   struct Channel *chptr;                /* Channel to show                          */
143   struct Client *acptr;         /* Client to show                           */
144
145   int bitsel;                   /* Mask of selectors to apply               */
146   int matchsel;                 /* Which fields the match should apply on    */
147   int counter;                  /* Query size counter,
148                                    initially used to count fields           */
149   int commas;                   /* Does our mask contain any comma ?
150                                    If so is a list..                        */
151   int fields;                   /* Mask of fields to show                   */
152   int isthere = 0;              /* When this set the user is member of chptr */
153   char *nick;                   /* Single element extracted from
154                                    the mask list                            */
155   char *p;                      /* Scratch char pointer                     */
156   char *qrt;                    /* Pointer to the query type                */
157   static char mymask[512];      /* To save the mask before corrupting it    */
158
159   /* Let's find where is our mask, and if actually contains something */
160   mask = ((parc > 1) ? parv[1] : 0);
161   if (parc > 3 && parv[3])
162     mask = parv[3];
163   if (mask && ((mask[0] == '\0') ||
164       (mask[1] == '\0' && ((mask[0] == '0') || (mask[0] == '*')))))
165     mask = 0;
166
167   /* Evaluate the flags now, we consider the second parameter 
168      as "matchFlags%fieldsToInclude,querytype"           */
169   bitsel = fields = counter = matchsel = 0;
170   qrt = 0;
171   if (parc > 2 && parv[2] && *parv[2])
172   {
173     p = parv[2];
174     while (((ch = *(p++))) && (ch != '%') && (ch != ','))
175       switch (ch)
176       {
177         case 'd':
178         case 'D':
179           bitsel |= WHOSELECT_DELAY;
180           continue;
181         case 'o':
182         case 'O':
183           bitsel |= WHOSELECT_OPER;
184           continue;
185         case 'x':
186         case 'X':
187           bitsel |= WHOSELECT_EXTRA;
188           if (HasPriv(sptr, PRIV_WHOX))
189             log_write(LS_WHO, L_INFO, LOG_NOSNOTICE, "%#C WHO %s %s", sptr,
190                       (BadPtr(parv[3]) ? parv[1] : parv[3]), parv[2]);
191           continue;
192         case 'n':
193         case 'N':
194           matchsel |= WHO_FIELD_NIC;
195           continue;
196         case 'u':
197         case 'U':
198           matchsel |= WHO_FIELD_UID;
199           continue;
200         case 'h':
201         case 'H':
202           matchsel |= WHO_FIELD_HOS;
203           continue;
204         case 'i':
205         case 'I':
206           matchsel |= WHO_FIELD_NIP;
207           continue;
208         case 's':
209         case 'S':
210           matchsel |= WHO_FIELD_SER;
211           continue;
212         case 'r':
213         case 'R':
214           matchsel |= WHO_FIELD_REN;
215           continue;
216         case 'a':
217         case 'A':
218           matchsel |= WHO_FIELD_ACC;
219           continue;
220       }
221     if (ch == '%')
222       while ((ch = *p++) && (ch != ','))
223       {
224         counter++;
225         switch (ch)
226         {
227           case 'c':
228           case 'C':
229             fields |= WHO_FIELD_CHA;
230             break;
231           case 'd':
232           case 'D':
233             fields |= WHO_FIELD_DIS;
234             break;
235           case 'f':
236           case 'F':
237             fields |= WHO_FIELD_FLA;
238             break;
239           case 'h':
240           case 'H':
241             fields |= WHO_FIELD_HOS;
242             break;
243           case 'i':
244           case 'I':
245             fields |= WHO_FIELD_NIP;
246             break;
247           case 'l':
248           case 'L':
249             fields |= WHO_FIELD_IDL;
250           case 'n':
251           case 'N':
252             fields |= WHO_FIELD_NIC;
253             break;
254           case 'r':
255           case 'R':
256             fields |= WHO_FIELD_REN;
257             break;
258           case 's':
259           case 'S':
260             fields |= WHO_FIELD_SER;
261             break;
262           case 't':
263           case 'T':
264             fields |= WHO_FIELD_QTY;
265             break;
266           case 'u':
267           case 'U':
268             fields |= WHO_FIELD_UID;
269             break;
270           case 'a':
271           case 'A':
272             fields |= WHO_FIELD_ACC;
273             break;
274           case 'o':
275           case 'O':
276             fields |= WHO_FIELD_OPL;
277             break;
278           default:
279             break;
280         }
281       };
282     if (ch)
283       qrt = p;
284   }
285
286   if (!matchsel)
287     matchsel = WHO_FIELD_DEF;
288   if (!fields)
289     counter = 7;
290
291   if (feature_bool(FEAT_HIS_WHO_SERVERNAME) && !IsAnOper(sptr))
292     matchsel &= ~WHO_FIELD_SER;
293
294   if (qrt && (fields & WHO_FIELD_QTY))
295   {
296     p = qrt;
297     if (!((*p > '9') || (*p < '0')))
298       p++;
299     if (!((*p > '9') || (*p < '0')))
300       p++;
301     if (!((*p > '9') || (*p < '0')))
302       p++;
303     *p = '\0';
304   }
305   else
306     qrt = 0;
307
308   /* I'd love to add also a check on the number of matches fields per time */
309   counter = (2048 / (counter + 4));
310   if (mask && (strlen(mask) > 510))
311     mask[510] = '\0';
312   move_marker();
313   commas = (mask && strchr(mask, ','));
314
315   /* First treat mask as a list of plain nicks/channels */
316   if (mask)
317   {
318     strcpy(mymask, mask);
319     for (p = 0, nick = ircd_strtok(&p, mymask, ","); nick;
320         nick = ircd_strtok(&p, 0, ","))
321     {
322       if (IsChannelName(nick) && (chptr = FindChannel(nick)))
323       {
324         isthere = (find_channel_member(sptr, chptr) != 0);
325         if (isthere || SEE_CHANNEL(sptr, chptr, bitsel))
326         {
327           struct Membership* member;
328           for (member = chptr->members; member; member = member->next_member)
329           {
330             acptr = member->user;
331             if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr))
332               continue;
333             if ((acptr != sptr)
334                 && ((member->status & CHFL_ZOMBIE)
335                     || ((member->status & CHFL_DELAYED)
336                         && !(bitsel & WHOSELECT_DELAY))))
337               continue;
338                         if ((acptr != sptr) && IsInvisibleJoin(member)) 
339                           continue;
340                         if((acptr != sptr) && (!IsChanOp(member) && !HasVoice(member) && (chptr->mode.mode & MODE_AUDITORIUM)))
341                           continue;
342             if (!(isthere || (SEE_USER(sptr, acptr, bitsel))))
343               continue;
344             if (!Process(acptr))        /* This can't be moved before other checks */
345               continue;
346             if (!(isthere || (SHOW_MORE(sptr, counter))))
347               break;
348             do_who(sptr, acptr, chptr, fields, qrt);
349           }
350         }
351       }
352       else
353       {
354         if ((acptr = FindUser(nick)) &&
355             ((!(bitsel & WHOSELECT_OPER)) || SeeOper(sptr,acptr)) &&
356             Process(acptr) && SHOW_MORE(sptr, counter))
357         {
358           do_who(sptr, acptr, 0, fields, qrt);
359         }
360       }
361     }
362   }
363
364   /* If we didn't have any comma in the mask treat it as a
365      real mask and try to match all relevant fields */
366   if (!(commas || (counter < 1)))
367   {
368     struct irc_in_addr imask;
369     int minlen, cset;
370     unsigned char ibits;
371
372     if (mask)
373     {
374       matchcomp(mymask, &minlen, &cset, mask);
375       if (!ipmask_parse(mask, &imask, &ibits))
376         matchsel &= ~WHO_FIELD_NIP;
377       if ((minlen > NICKLEN) || !(cset & NTL_IRCNK))
378         matchsel &= ~WHO_FIELD_NIC;
379       if ((matchsel & WHO_FIELD_SER) &&
380           ((minlen > HOSTLEN) || (!(cset & NTL_IRCHN))
381           || (!markMatchexServer(mymask, minlen))))
382         matchsel &= ~WHO_FIELD_SER;
383       if ((minlen > USERLEN) || !(cset & NTL_IRCUI))
384         matchsel &= ~WHO_FIELD_UID;
385       if ((minlen > HOSTLEN) || !(cset & NTL_IRCHN))
386         matchsel &= ~WHO_FIELD_HOS;
387       if ((minlen > ACCOUNTLEN))
388         matchsel &= ~WHO_FIELD_ACC;
389     }
390
391     /* First of all loop through the clients in common channels */
392     if ((!(counter < 1)) && matchsel) {
393       struct Membership* member;
394       struct Membership* chan;
395       for (chan = cli_user(sptr)->channel; chan; chan = chan->next_channel) {
396         chptr = chan->channel;
397         for (member = chptr->members; member; member = member->next_member)
398         {
399           acptr = member->user;
400           if (!(IsUser(acptr) && Process(acptr)))
401             continue;           /* Now Process() is at the beginning, if we fail
402                                    we'll never have to show this acptr in this query */
403           if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr))
404             continue;
405           if ((mask) &&
406               ((!(matchsel & WHO_FIELD_NIC))
407               || matchexec(cli_name(acptr), mymask, minlen))
408               && ((!(matchsel & WHO_FIELD_UID))
409               || matchexec(cli_user(acptr)->username, mymask, minlen))
410               && ((!(matchsel & WHO_FIELD_SER))
411               || (!(HasFlag(cli_user(acptr)->server, FLAG_MAP))))
412               && ((!(matchsel & WHO_FIELD_HOS))
413               || matchexec(cli_user(acptr)->host, mymask, minlen))
414               && ((!(matchsel & WHO_FIELD_HOS))
415               || !HasHiddenHost(acptr)
416               || !IsAnOper(sptr)
417               || matchexec(cli_user(acptr)->realhost, mymask, minlen))
418               && ((!(matchsel & WHO_FIELD_REN))
419               || matchexec(cli_info(acptr), mymask, minlen))
420               && ((!(matchsel & WHO_FIELD_NIP))
421               || (HasHiddenHost(acptr) && !IsAnOper(sptr))
422               || !ipmask_check(&cli_ip(acptr), &imask, ibits))
423               && ((!(matchsel & WHO_FIELD_ACC))
424               || matchexec(cli_user(acptr)->account, mymask, minlen)))
425             continue;
426           if (!SHOW_MORE(sptr, counter))
427             break;
428           do_who(sptr, acptr, chptr, fields, qrt);
429         }
430       }
431     }
432     /* Loop through all clients :-\, if we still have something to match to 
433        and we can show more clients */
434     if ((!(counter < 1)) && matchsel)
435       for (acptr = cli_prev(&me); acptr; acptr = cli_prev(acptr))
436       {
437         if (!(IsUser(acptr) && Process(acptr)))
438           continue;
439         if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr))
440           continue;
441         if (!(SEE_USER(sptr, acptr, bitsel)))
442           continue;
443         if ((mask) &&
444             ((!(matchsel & WHO_FIELD_NIC))
445             || matchexec(cli_name(acptr), mymask, minlen))
446             && ((!(matchsel & WHO_FIELD_UID))
447             || matchexec(cli_user(acptr)->username, mymask, minlen))
448             && ((!(matchsel & WHO_FIELD_SER))
449                 || (!(HasFlag(cli_user(acptr)->server, FLAG_MAP))))
450             && ((!(matchsel & WHO_FIELD_HOS))
451             || matchexec(cli_user(acptr)->host, mymask, minlen))
452             && ((!(matchsel & WHO_FIELD_HOS))
453             || !HasHiddenHost(acptr)
454             || !IsAnOper(sptr)
455             || matchexec(cli_user(acptr)->realhost, mymask, minlen))
456             && ((!(matchsel & WHO_FIELD_REN))
457             || matchexec(cli_info(acptr), mymask, minlen))
458             && ((!(matchsel & WHO_FIELD_NIP))
459             || (HasHiddenHost(acptr) && !IsAnOper(sptr))
460             || !ipmask_check(&cli_ip(acptr), &imask, ibits))
461             && ((!(matchsel & WHO_FIELD_ACC))
462             || matchexec(cli_user(acptr)->account, mymask, minlen)))
463           continue;
464         if (!SHOW_MORE(sptr, counter))
465           break;
466         do_who(sptr, acptr, 0, fields, qrt);
467       }
468   }
469
470   /* Make a clean mask suitable to be sent in the "end of" */
471   if (mask && (p = strchr(mask, ' ')))
472     *p = '\0';
473   /* Notify the user if we decided that his query was too long */
474   if (counter < 0)
475     send_reply(sptr, ERR_QUERYTOOLONG, BadPtr(mask) ? "*" : mask);
476   send_reply(sptr, RPL_ENDOFWHO, BadPtr(mask) ? "*" : mask);
477
478   return 0;
479 }