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