Author: Bleep <tomh@inxpress.net>
[ircu2.10.12-pk.git] / ircd / m_whois.c
1 /*
2  * IRC - Internet Relay Chat, ircd/m_whois.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_reply.h"
95 #include "ircd_string.h"
96 #include "match.h"
97 #include "msg.h"
98 #include "numeric.h"
99 #include "numnicks.h"
100 #include "s_user.h"
101 #include "send.h"
102 #include "whocmds.h"
103
104 #include <assert.h>
105 #include <string.h>
106
107 /*
108  * m_whois - generic message handler
109  *
110  * parv[0] = sender prefix
111  * parv[1] = nickname masklist
112  *
113  * or
114  *
115  * parv[1] = target server
116  * parv[2] = nickname masklist
117  */
118 int m_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
119 {
120   struct User*    user;
121   struct Client*  acptr;
122   struct Client*  a2cptr;
123   struct Channel* chptr;
124   char*           nick;
125   char*           tmp;
126   char*           name;
127   char*           p = 0;
128   int             found;
129   int             len;
130   int             mlen;
131   int             total;
132   static char     buf[512];
133
134   if (parc < 2)
135   {
136     sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]);
137     return 0;
138   }
139
140   if (parc > 2)
141   {
142     struct Client *acptr;
143     /* For convenience: Accept a nickname as first parameter, by replacing
144        it with the correct servername - as is needed by hunt_server() */
145     if (MyUser(sptr) && (acptr = FindUser(parv[1])))
146       parv[1] = acptr->user->server->name;
147     if (hunt_server(0, cptr, sptr, "%s%s " TOK_WHOIS " %s :%s", 1, parc, parv) !=
148         HUNTED_ISME)
149       return 0;
150     parv[1] = parv[2];
151   }
152
153   total = 0;
154   for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0)
155   {
156     int invis, showperson, member, wilds;
157
158     found = 0;
159     collapse(nick);
160     wilds = (strchr(nick, '?') || strchr(nick, '*'));
161     /* Do a hash lookup if the nick does not contain wilds */
162     if (wilds)
163     {
164       /*
165        * We're no longer allowing remote users to generate requests with wildcards.
166        */
167       if (!MyConnect(sptr))
168         continue;
169       for (acptr = GlobalClientList; (acptr = next_client(acptr, nick));
170           acptr = acptr->next)
171       {
172         if (!IsRegistered(acptr) || IsServer(acptr))
173           continue;
174         /*
175          * I'm always last :-) and acptr->next == 0!!
176          */
177         if (IsMe(acptr))
178           break;
179         /*
180          * 'Rules' established for sending a WHOIS reply:
181          *
182          * - if wildcards are being used dont send a reply if
183          *   the querier isnt any common channels and the
184          *   client in question is invisible and wildcards are
185          *   in use (allow exact matches only);
186          *
187          * - only send replies about common or public channels
188          *   the target user(s) are on;
189          */
190         user = acptr->user;
191         name = (!*acptr->name) ? "?" : acptr->name;
192
193         invis = acptr != sptr && IsInvisible(acptr);
194         member = (user && user->channel) ? 1 : 0;
195         showperson = (wilds && !invis && !member) || !wilds;
196         if (user) {
197           struct Membership* chan;
198           for (chan = user->channel; chan; chan = chan->next_channel)
199           {
200             chptr = chan->channel;
201             member = find_channel_member(sptr, chptr) ? 1 : 0;
202             if (invis && !member)
203               continue;
204             if (IsZombie(chan))
205               continue;
206             if (member || (!invis && PubChannel(chptr)))
207             {
208               showperson = 1;
209               break;
210             }
211             if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
212               showperson = 1;
213           }
214         }
215         if (!showperson)
216           continue;
217
218         if (user)
219         {
220           a2cptr = user->server;
221           sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
222               parv[0], name, user->username, user->host, acptr->info);
223         }
224         else
225         {
226           a2cptr = &me;
227           sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
228               parv[0], name, "<unknown>", "<unknown>", "<unknown>");
229         }
230
231         found = 1;
232
233 exact_match:
234         if (user && !IsChannelService(acptr))
235         {
236           struct Membership* chan;
237           mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name);
238           len = 0;
239           *buf = '\0';
240           for (chan = user->channel; chan; chan = chan->next_channel)
241           {
242             chptr = chan->channel;
243             if (ShowChannel(sptr, chptr) &&
244                 (acptr == sptr || !IsZombie(chan)))
245             {
246               if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5)
247               {
248                 sendto_one(sptr, ":%s %d %s %s :%s",
249                     me.name, RPL_WHOISCHANNELS, parv[0], name, buf);
250                 *buf = '\0';
251                 len = 0;
252               }
253               if (IsDeaf(acptr))
254                 *(buf + len++) = '-';
255               if (is_chan_op(acptr, chptr))
256                 *(buf + len++) = '@';
257               else if (has_voice(acptr, chptr))
258                 *(buf + len++) = '+';
259               else if (IsZombie(chan))
260                 *(buf + len++) = '!';
261               if (len)
262                 *(buf + len) = '\0';
263               strcpy(buf + len, chptr->chname);
264               len += strlen(chptr->chname);
265               strcat(buf + len, " ");
266               len++;
267             }
268           }
269           if (buf[0] != '\0')
270             sendto_one(sptr, rpl_str(RPL_WHOISCHANNELS),
271                 me.name, parv[0], name, buf);
272         }
273
274         sendto_one(sptr, rpl_str(RPL_WHOISSERVER), me.name,
275             parv[0], name, a2cptr->name, a2cptr->info);
276
277         if (user)
278         {
279           if (user->away)
280             sendto_one(sptr, rpl_str(RPL_AWAY), me.name,
281                 parv[0], name, user->away);
282
283           if (IsAnOper(acptr))
284             sendto_one(sptr, rpl_str(RPL_WHOISOPERATOR),
285                 me.name, parv[0], name);
286
287           if (MyConnect(acptr))
288             sendto_one(sptr, rpl_str(RPL_WHOISIDLE), me.name,
289                 parv[0], name, CurrentTime - user->last, acptr->firsttime);
290         }
291         if (found == 2 || total++ >= MAX_WHOIS_LINES)
292           break;
293       }
294     }
295     else
296     {
297       /* No wildcards */
298       if ((acptr = FindUser(nick)))
299       {
300         found = 2;              /* Make sure we exit the loop after passing it once */
301         user = acptr->user;
302         name = (!*acptr->name) ? "?" : acptr->name;
303         a2cptr = user->server;
304         sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
305             parv[0], name, user->username, user->host, acptr->info);
306         goto exact_match;
307       }
308     }
309     if (!found)
310       sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick);
311     if (p)
312       p[-1] = ',';
313     if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES)
314       break;
315   }
316   sendto_one(sptr, rpl_str(RPL_ENDOFWHOIS), me.name, parv[0], parv[1]);
317
318   return 0;
319 }
320
321 /*
322  * ms_whois - server message handler
323  *
324  * parv[0] = sender prefix
325  * parv[1] = nickname masklist
326  *
327  * or
328  *
329  * parv[1] = target server
330  * parv[2] = nickname masklist
331  */
332 int ms_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
333 {
334   struct User*    user;
335   struct Client*  acptr;
336   struct Client*  a2cptr;
337   struct Channel* chptr;
338   char*           nick;
339   char*           tmp;
340   char*           name;
341   char*           p = 0;
342   int             found;
343   int             len;
344   int             mlen;
345   int             total;
346   static char     buf[512];
347
348   if (parc < 2)
349   {
350     sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]);
351     return 0;
352   }
353
354   if (parc > 2)
355   {
356     struct Client *acptr;
357     /* For convenience: Accept a nickname as first parameter, by replacing
358        it with the correct servername - as is needed by hunt_server() */
359     if (MyUser(sptr) && (acptr = FindUser(parv[1])))
360       parv[1] = acptr->user->server->name;
361     if (hunt_server(0, cptr, sptr, "%s%s " TOK_WHOIS " %s :%s", 1, parc, parv) !=
362         HUNTED_ISME)
363       return 0;
364     parv[1] = parv[2];
365   }
366
367   total = 0;
368   for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0)
369   {
370     int invis, showperson, member, wilds;
371
372     found = 0;
373     collapse(nick);
374     wilds = (strchr(nick, '?') || strchr(nick, '*'));
375     /* Do a hash lookup if the nick does not contain wilds */
376     if (wilds)
377     {
378       /*
379        * We're no longer allowing remote users to generate requests with wildcards.
380        */
381       if (!MyConnect(sptr))
382         continue;
383       for (acptr = GlobalClientList; (acptr = next_client(acptr, nick));
384           acptr = acptr->next)
385       {
386         if (!IsRegistered(acptr) || IsServer(acptr))
387           continue;
388         /*
389          * I'm always last :-) and acptr->next == 0!!
390          */
391         if (IsMe(acptr))
392           break;
393         /*
394          * 'Rules' established for sending a WHOIS reply:
395          *
396          * - if wildcards are being used dont send a reply if
397          *   the querier isnt any common channels and the
398          *   client in question is invisible and wildcards are
399          *   in use (allow exact matches only);
400          *
401          * - only send replies about common or public channels
402          *   the target user(s) are on;
403          */
404         user = acptr->user;
405         name = (!*acptr->name) ? "?" : acptr->name;
406
407         invis = acptr != sptr && IsInvisible(acptr);
408         member = (user && user->channel) ? 1 : 0;
409         showperson = (wilds && !invis && !member) || !wilds;
410         if (user) {
411           struct Membership* chan;
412           for (chan = user->channel; chan; chan = chan->next_channel)
413           {
414             chptr = chan->channel;
415             member = find_channel_member(sptr, chptr) ? 1 : 0;
416             if (invis && !member)
417               continue;
418             if (IsZombie(chan))
419               continue;
420             if (member || (!invis && PubChannel(chptr)))
421             {
422               showperson = 1;
423               break;
424             }
425             if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
426               showperson = 1;
427           }
428         }
429         if (!showperson)
430           continue;
431
432         if (user)
433         {
434           a2cptr = user->server;
435           sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
436               parv[0], name, user->username, user->host, acptr->info);
437         }
438         else
439         {
440           a2cptr = &me;
441           sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
442               parv[0], name, "<unknown>", "<unknown>", "<unknown>");
443         }
444
445         found = 1;
446
447 exact_match:
448         if (user && !IsChannelService(acptr))
449         {
450           struct Membership* chan;
451           mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name);
452           len = 0;
453           *buf = '\0';
454           for (chan = user->channel; chan; chan = chan->next_channel)
455           {
456             chptr = chan->channel;
457             if (ShowChannel(sptr, chptr) &&
458                 (acptr == sptr || !IsZombie(chan)))
459             {
460               if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5)
461               {
462                 sendto_one(sptr, ":%s %d %s %s :%s",
463                     me.name, RPL_WHOISCHANNELS, parv[0], name, buf);
464                 *buf = '\0';
465                 len = 0;
466               }
467               if (IsDeaf(acptr))
468                 *(buf + len++) = '-';
469               if (is_chan_op(acptr, chptr))
470                 *(buf + len++) = '@';
471               else if (has_voice(acptr, chptr))
472                 *(buf + len++) = '+';
473               else if (IsZombie(chan))
474                 *(buf + len++) = '!';
475               if (len)
476                 *(buf + len) = '\0';
477               strcpy(buf + len, chptr->chname);
478               len += strlen(chptr->chname);
479               strcat(buf + len, " ");
480               len++;
481             }
482           }
483           if (buf[0] != '\0')
484             sendto_one(sptr, rpl_str(RPL_WHOISCHANNELS),
485                 me.name, parv[0], name, buf);
486         }
487
488         sendto_one(sptr, rpl_str(RPL_WHOISSERVER), me.name,
489             parv[0], name, a2cptr->name, a2cptr->info);
490
491         if (user)
492         {
493           if (user->away)
494             sendto_one(sptr, rpl_str(RPL_AWAY), me.name,
495                 parv[0], name, user->away);
496
497           if (IsAnOper(acptr))
498             sendto_one(sptr, rpl_str(RPL_WHOISOPERATOR),
499                 me.name, parv[0], name);
500
501           if (MyConnect(acptr))
502             sendto_one(sptr, rpl_str(RPL_WHOISIDLE), me.name,
503                 parv[0], name, CurrentTime - user->last, acptr->firsttime);
504         }
505         if (found == 2 || total++ >= MAX_WHOIS_LINES)
506           break;
507       }
508     }
509     else
510     {
511       /* No wildcards */
512       if ((acptr = FindUser(nick)))
513       {
514         found = 2;              /* Make sure we exit the loop after passing it once */
515         user = acptr->user;
516         name = (!*acptr->name) ? "?" : acptr->name;
517         a2cptr = user->server;
518         sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
519             parv[0], name, user->username, user->host, acptr->info);
520         goto exact_match;
521       }
522     }
523     if (!found)
524       sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick);
525     if (p)
526       p[-1] = ',';
527     if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES)
528       break;
529   }
530   sendto_one(sptr, rpl_str(RPL_ENDOFWHOIS), me.name, parv[0], parv[1]);
531
532   return 0;
533 }
534
535 /*
536  * mo_whois - oper message handler
537  *
538  * parv[0] = sender prefix
539  * parv[1] = nickname masklist
540  *
541  * or
542  *
543  * parv[1] = target server
544  * parv[2] = nickname masklist
545  */
546 int mo_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
547 {
548   struct User*    user;
549   struct Client*  acptr;
550   struct Client*  a2cptr;
551   struct Channel* chptr;
552   char*           nick;
553   char*           tmp;
554   char*           name;
555   char*           p = 0;
556   int             found;
557   int             len;
558   int             mlen;
559   int             total;
560   static char     buf[512];
561
562   if (parc < 2)
563   {
564     sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]);
565     return 0;
566   }
567
568   if (parc > 2)
569   {
570     struct Client *acptr;
571     /* For convenience: Accept a nickname as first parameter, by replacing
572        it with the correct servername - as is needed by hunt_server() */
573     if (MyUser(sptr) && (acptr = FindUser(parv[1])))
574       parv[1] = acptr->user->server->name;
575     if (hunt_server(0, cptr, sptr, "%s%s " TOK_WHOIS " %s :%s", 1, parc, parv) !=
576         HUNTED_ISME)
577       return 0;
578     parv[1] = parv[2];
579   }
580
581   total = 0;
582   for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0)
583   {
584     int invis, showperson, member, wilds;
585
586     found = 0;
587     collapse(nick);
588     wilds = (strchr(nick, '?') || strchr(nick, '*'));
589     /* Do a hash lookup if the nick does not contain wilds */
590     if (wilds)
591     {
592       /*
593        * We're no longer allowing remote users to generate requests with wildcards.
594        */
595       if (!MyConnect(sptr))
596         continue;
597       for (acptr = GlobalClientList; (acptr = next_client(acptr, nick));
598           acptr = acptr->next)
599       {
600         if (!IsRegistered(acptr) || IsServer(acptr))
601           continue;
602         /*
603          * I'm always last :-) and acptr->next == 0!!
604          */
605         if (IsMe(acptr))
606           break;
607         /*
608          * 'Rules' established for sending a WHOIS reply:
609          *
610          * - if wildcards are being used dont send a reply if
611          *   the querier isnt any common channels and the
612          *   client in question is invisible and wildcards are
613          *   in use (allow exact matches only);
614          *
615          * - only send replies about common or public channels
616          *   the target user(s) are on;
617          */
618         user = acptr->user;
619         name = (!*acptr->name) ? "?" : acptr->name;
620
621         invis = acptr != sptr && IsInvisible(acptr);
622         member = (user && user->channel) ? 1 : 0;
623         showperson = (wilds && !invis && !member) || !wilds;
624         if (user) {
625           struct Membership* chan;
626           for (chan = user->channel; chan; chan = chan->next_channel)
627           {
628             chptr = chan->channel;
629             member = find_channel_member(sptr, chptr) ? 1 : 0;
630             if (invis && !member)
631               continue;
632             if (IsZombie(chan))
633               continue;
634             if (member || (!invis && PubChannel(chptr)))
635             {
636               showperson = 1;
637               break;
638             }
639             if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
640               showperson = 1;
641           }
642         }
643         if (!showperson)
644           continue;
645
646         if (user)
647         {
648           a2cptr = user->server;
649           sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
650               parv[0], name, user->username, user->host, acptr->info);
651         }
652         else
653         {
654           a2cptr = &me;
655           sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
656               parv[0], name, "<unknown>", "<unknown>", "<unknown>");
657         }
658
659         found = 1;
660
661 exact_match:
662         if (user && !IsChannelService(acptr))
663         {
664           struct Membership* chan;
665           mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name);
666           len = 0;
667           *buf = '\0';
668           for (chan = user->channel; chan; chan = chan->next_channel)
669           {
670             chptr = chan->channel;
671             if (ShowChannel(sptr, chptr) &&
672                 (acptr == sptr || !IsZombie(chan)))
673             {
674               if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5)
675               {
676                 sendto_one(sptr, ":%s %d %s %s :%s",
677                     me.name, RPL_WHOISCHANNELS, parv[0], name, buf);
678                 *buf = '\0';
679                 len = 0;
680               }
681               if (IsDeaf(acptr))
682                 *(buf + len++) = '-';
683               if (is_chan_op(acptr, chptr))
684                 *(buf + len++) = '@';
685               else if (has_voice(acptr, chptr))
686                 *(buf + len++) = '+';
687               else if (IsZombie(chan))
688                 *(buf + len++) = '!';
689               if (len)
690                 *(buf + len) = '\0';
691               strcpy(buf + len, chptr->chname);
692               len += strlen(chptr->chname);
693               strcat(buf + len, " ");
694               len++;
695             }
696           }
697           if (buf[0] != '\0')
698             sendto_one(sptr, rpl_str(RPL_WHOISCHANNELS),
699                 me.name, parv[0], name, buf);
700         }
701
702         sendto_one(sptr, rpl_str(RPL_WHOISSERVER), me.name,
703             parv[0], name, a2cptr->name, a2cptr->info);
704
705         if (user)
706         {
707           if (user->away)
708             sendto_one(sptr, rpl_str(RPL_AWAY), me.name,
709                 parv[0], name, user->away);
710
711           if (IsAnOper(acptr))
712             sendto_one(sptr, rpl_str(RPL_WHOISOPERATOR),
713                 me.name, parv[0], name);
714
715           if (MyConnect(acptr))
716             sendto_one(sptr, rpl_str(RPL_WHOISIDLE), me.name,
717                 parv[0], name, CurrentTime - user->last, acptr->firsttime);
718         }
719         if (found == 2 || total++ >= MAX_WHOIS_LINES)
720           break;
721       }
722     }
723     else
724     {
725       /* No wildcards */
726       if ((acptr = FindUser(nick)))
727       {
728         found = 2;              /* Make sure we exit the loop after passing it once */
729         user = acptr->user;
730         name = (!*acptr->name) ? "?" : acptr->name;
731         a2cptr = user->server;
732         sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
733             parv[0], name, user->username, user->host, acptr->info);
734         goto exact_match;
735       }
736     }
737     if (!found)
738       sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick);
739     if (p)
740       p[-1] = ',';
741     if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES)
742       break;
743   }
744   sendto_one(sptr, rpl_str(RPL_ENDOFWHOIS), me.name, parv[0], parv[1]);
745
746   return 0;
747 }
748
749   
750
751 #if 0
752 /*
753  * m_whois
754  *
755  * parv[0] = sender prefix
756  * parv[1] = nickname masklist
757  *
758  * or
759  *
760  * parv[1] = target server
761  * parv[2] = nickname masklist
762  */
763 int m_whois(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
764 {
765   struct User*    user;
766   struct Client*  acptr;
767   struct Client*  a2cptr;
768   struct Channel* chptr;
769   char*           nick;
770   char*           tmp;
771   char*           name;
772   char*           p = 0;
773   int             found;
774   int             len;
775   int             mlen;
776   int             total;
777   static char     buf[512];
778
779   if (parc < 2)
780   {
781     sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]);
782     return 0;
783   }
784
785   if (parc > 2)
786   {
787     struct Client *acptr;
788     /* For convenience: Accept a nickname as first parameter, by replacing
789        it with the correct servername - as is needed by hunt_server() */
790     if (MyUser(sptr) && (acptr = FindUser(parv[1])))
791       parv[1] = acptr->user->server->name;
792     if (hunt_server(0, cptr, sptr, "%s%s " TOK_WHOIS " %s :%s", 1, parc, parv) !=
793         HUNTED_ISME)
794       return 0;
795     parv[1] = parv[2];
796   }
797
798   total = 0;
799   for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0)
800   {
801     int invis, showperson, member, wilds;
802
803     found = 0;
804     collapse(nick);
805     wilds = (strchr(nick, '?') || strchr(nick, '*'));
806     /* Do a hash lookup if the nick does not contain wilds */
807     if (wilds)
808     {
809       /*
810        * We're no longer allowing remote users to generate requests with wildcards.
811        */
812       if (!MyConnect(sptr))
813         continue;
814       for (acptr = GlobalClientList; (acptr = next_client(acptr, nick));
815           acptr = acptr->next)
816       {
817         if (!IsRegistered(acptr) || IsServer(acptr))
818           continue;
819         /*
820          * I'm always last :-) and acptr->next == NULL!!
821          */
822         if (IsMe(acptr))
823           break;
824         /*
825          * 'Rules' established for sending a WHOIS reply:
826          *
827          * - if wildcards are being used dont send a reply if
828          *   the querier isnt any common channels and the
829          *   client in question is invisible and wildcards are
830          *   in use (allow exact matches only);
831          *
832          * - only send replies about common or public channels
833          *   the target user(s) are on;
834          */
835         user = acptr->user;
836         name = (!*acptr->name) ? "?" : acptr->name;
837
838         invis = acptr != sptr && IsInvisible(acptr);
839         member = (user && user->channel) ? 1 : 0;
840         showperson = (wilds && !invis && !member) || !wilds;
841         if (user) {
842           struct Membership* chan;
843           for (chan = user->channel; chan; chan = chan->next_channel)
844           {
845             chptr = chan->channel;
846             member = find_channel_member(sptr, chptr) ? 1 : 0;
847             if (invis && !member)
848               continue;
849             if (IsZombie(chan))
850               continue;
851             if (member || (!invis && PubChannel(chptr)))
852             {
853               showperson = 1;
854               break;
855             }
856             if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
857               showperson = 1;
858           }
859         }
860         if (!showperson)
861           continue;
862
863         if (user)
864         {
865           a2cptr = user->server;
866           sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
867               parv[0], name, user->username, user->host, acptr->info);
868         }
869         else
870         {
871           a2cptr = &me;
872           sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
873               parv[0], name, "<unknown>", "<unknown>", "<unknown>");
874         }
875
876         found = 1;
877
878 exact_match:
879         if (user && !IsChannelService(acptr))
880         {
881           struct Membership* chan;
882           mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name);
883           len = 0;
884           *buf = '\0';
885           for (chan = user->channel; chan; chan = chan->next_channel)
886           {
887             chptr = chan->channel;
888             if (ShowChannel(sptr, chptr) &&
889                 (acptr == sptr || !IsZombie(chan)))
890             {
891               if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5)
892               {
893                 sendto_one(sptr, ":%s %d %s %s :%s",
894                     me.name, RPL_WHOISCHANNELS, parv[0], name, buf);
895                 *buf = '\0';
896                 len = 0;
897               }
898               if (IsDeaf(acptr))
899                 *(buf + len++) = '-';
900               if (is_chan_op(acptr, chptr))
901                 *(buf + len++) = '@';
902               else if (has_voice(acptr, chptr))
903                 *(buf + len++) = '+';
904               else if (IsZombie(chan))
905                 *(buf + len++) = '!';
906               if (len)
907                 *(buf + len) = '\0';
908               strcpy(buf + len, chptr->chname);
909               len += strlen(chptr->chname);
910               strcat(buf + len, " ");
911               len++;
912             }
913           }
914           if (buf[0] != '\0')
915             sendto_one(sptr, rpl_str(RPL_WHOISCHANNELS),
916                 me.name, parv[0], name, buf);
917         }
918
919         sendto_one(sptr, rpl_str(RPL_WHOISSERVER), me.name,
920             parv[0], name, a2cptr->name, a2cptr->info);
921
922         if (user)
923         {
924           if (user->away)
925             sendto_one(sptr, rpl_str(RPL_AWAY), me.name,
926                 parv[0], name, user->away);
927
928           if (IsAnOper(acptr))
929             sendto_one(sptr, rpl_str(RPL_WHOISOPERATOR),
930                 me.name, parv[0], name);
931
932           if (MyConnect(acptr))
933             sendto_one(sptr, rpl_str(RPL_WHOISIDLE), me.name,
934                 parv[0], name, CurrentTime - user->last, acptr->firsttime);
935         }
936         if (found == 2 || total++ >= MAX_WHOIS_LINES)
937           break;
938       }
939     }
940     else
941     {
942       /* No wildcards */
943       if ((acptr = FindUser(nick)))
944       {
945         found = 2;              /* Make sure we exit the loop after passing it once */
946         user = acptr->user;
947         name = (!*acptr->name) ? "?" : acptr->name;
948         a2cptr = user->server;
949         sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
950             parv[0], name, user->username, user->host, acptr->info);
951         goto exact_match;
952       }
953     }
954     if (!found)
955       sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick);
956     if (p)
957       p[-1] = ',';
958     if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES)
959       break;
960   }
961   sendto_one(sptr, rpl_str(RPL_ENDOFWHOIS), me.name, parv[0], parv[1]);
962
963   return 0;
964 }
965 #endif /* 0 */
966