Author: Kev <klmitch@mit.edu>
[ircu2.10.12-pk.git] / ircd / m_names.c
1 /*
2  * IRC - Internet Relay Chat, ircd/m_names.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 #include "config.h"
83
84 #if 0
85 /*
86  * No need to include handlers.h here the signatures must match
87  * and we don't need to force a rebuild of all the handlers everytime
88  * we add a new one to the list. --Bleep
89  */
90 #include "handlers.h"
91 #endif /* 0 */
92 #include "channel.h"
93 #include "client.h"
94 #include "hash.h"
95 #include "ircd.h"
96 #include "ircd_reply.h"
97 #include "ircd_string.h"
98 #include "msg.h"
99 #include "numeric.h"
100 #include "numnicks.h"
101 #include "s_user.h"
102 #include "send.h"
103  
104 #include <assert.h>
105 #include <string.h>
106
107 /*
108  *  Sends a suitably formatted 'names' reply to 'sptr' consisting of nicks within
109  *  'chptr', depending on 'filter'.
110  *
111  *  NAMES_ALL - Lists all users on channel.
112  *  NAMES_VIS - Only list visible (-i) users. --Gte (04/06/2000).
113  *  NAMES_EON - When OR'd with the other two, adds an 'End of Names' numeric
114  *              used by m_join
115  *
116  */
117
118 void do_names(struct Client* sptr, struct Channel* chptr, int filter)
119
120   int mlen;
121   int idx;
122   int flag;
123   int needs_space; 
124   int len; 
125   char buf[BUFSIZE];
126   struct Client *c2ptr;
127   struct Membership* member;
128   
129   assert(chptr);
130   assert(sptr);
131   assert((filter&NAMES_ALL) != (filter&NAMES_VIS));
132
133   /* Tag Pub/Secret channels accordingly. */
134
135   strcpy(buf, "* ");
136   if (PubChannel(chptr))
137     *buf = '=';
138   else if (SecretChannel(chptr))
139     *buf = '@';
140  
141   len = strlen(chptr->chname);
142   strcpy(buf + 2, chptr->chname);
143   strcpy(buf + 2 + len, " :");
144
145   idx = len + 4;
146   flag = 1;
147   needs_space = 0;
148
149   if (!ShowChannel(sptr, chptr)) /* Don't list private channels unless we are on them. */
150     return;
151
152   /* Iterate over all channel members, and build up the list. */
153
154   mlen = strlen(cli_name(&me)) + 10 + strlen(cli_name(sptr));
155   
156   for (member = chptr->members; member; member = member->next_member)
157   {
158     c2ptr = member->user;
159
160     if (((filter&NAMES_VIS)!=0) && IsInvisible(c2ptr))
161       continue;
162
163     if (IsZombie(member) && member->user != sptr)
164       continue;
165
166     if (needs_space) {
167         strcat(buf, " ");
168       idx++;
169     }
170     needs_space=1;
171     if (IsZombie(member))
172     {
173       strcat(buf, "!");
174       idx++;
175     }
176     else if (IsChanOp(member))
177     {
178       strcat(buf, "@");
179       idx++;
180     }
181     else if (HasVoice(member))
182     {
183       strcat(buf, "+");
184       idx++;
185     }
186     strcat(buf, cli_name(c2ptr));
187     idx += strlen(cli_name(c2ptr)) + 1;
188     flag = 1;
189     if (mlen + idx + NICKLEN + 5 > BUFSIZE)
190       /* space, modifier, nick, \r \n \0 */
191     { 
192       send_reply(sptr, RPL_NAMREPLY, buf);
193       strcpy(buf, "* ");
194       ircd_strncpy(buf + 2, chptr->chname, len + 1);
195       buf[len + 2] = 0;
196       strcat(buf, " :");
197       if (PubChannel(chptr))
198         *buf = '=';
199       else if (SecretChannel(chptr))
200         *buf = '@';
201       idx = len + 4;
202       flag = 0;
203       needs_space=0;
204     }
205   }
206   if (flag)
207     send_reply(sptr, RPL_NAMREPLY, buf); 
208   if (filter&NAMES_EON)
209     send_reply(sptr, RPL_ENDOFNAMES, chptr->chname);
210 }
211
212 /*
213  * m_names - generic message handler
214  *
215  * parv[0] = sender prefix
216  * parv[1] = channel
217  */
218
219 int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
220 {
221   struct Channel *chptr; 
222   struct Channel *ch2ptr; 
223   struct Client *c2ptr;
224   struct Membership* member; 
225   char* s;
226   char* para = parc > 1 ? parv[1] : 0; 
227
228   if (parc > 2 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %C", 2, parc, parv))
229     return 0; 
230
231   if (EmptyString(para)) {
232     send_reply(sptr, RPL_ENDOFNAMES, "*");
233     return 0;
234   }
235   else if (*para == '0')
236     *para = '\0';
237   
238   s = strchr(para, ','); /* Recursively call m_names for each comma-seperated channel. Eww. */
239   if (s) {
240     parv[1] = ++s;
241     m_names(cptr, sptr, parc, parv);
242   }
243  
244   /*
245    * Special Case 1: "/names 0". 
246    * Full list as per RFC. 
247    */
248
249   if (!*para) { 
250     int idx; 
251     int mlen;
252     int flag;
253     struct Channel *ch3ptr;
254     char buf[BUFSIZE]; 
255
256     mlen = strlen(cli_name(&me)) + 10 + strlen(cli_name(sptr));
257
258     /* List all visible channels/visible members */ 
259
260     for (ch2ptr = GlobalChannelList; ch2ptr; ch2ptr = ch2ptr->next)
261     { 
262       if (!ShowChannel(sptr, ch2ptr))
263         continue;                 /* Don't show secret chans. */ 
264
265       if (find_channel_member(sptr, ch2ptr))
266       {
267         do_names(sptr, ch2ptr, NAMES_ALL); /* Full list if we're in this chan. */
268       } else { 
269         do_names(sptr, ch2ptr, NAMES_VIS);
270       }
271     } 
272
273     /* List all remaining users on channel '*' */
274
275     strcpy(buf, "* * :");
276     idx = 5;
277     flag = 0;
278
279     for (c2ptr = GlobalClientList; c2ptr; c2ptr = cli_next(c2ptr))
280     {
281       int showflag = 0;
282
283       if (!IsUser(c2ptr) || (sptr != c2ptr && IsInvisible(c2ptr)))
284         continue;
285
286       member = cli_user(c2ptr)->channel;
287
288       while (member)
289       {
290         ch3ptr = member->channel;
291   
292         if (PubChannel(ch3ptr) || find_channel_member(sptr, ch3ptr))
293           showflag = 1;
294  
295         member = member->next_channel;
296       }
297
298       if (showflag)               /* Have we already shown them? */
299         continue;
300  
301       strcat(buf, cli_name(c2ptr));
302       strcat(buf, " ");
303       idx += strlen(cli_name(c2ptr)) + 1;
304       flag = 1;
305
306       if (mlen + idx + NICKLEN + 3 > BUFSIZE)     /* space, \r\n\0 */
307       {
308         send_reply(sptr, RPL_NAMREPLY, buf);
309         strcpy(buf, "* * :");
310         idx = 5;
311         flag = 0;
312       }
313     }
314     if (flag)
315       send_reply(sptr, RPL_NAMREPLY, buf);
316     send_reply(sptr, RPL_ENDOFNAMES, "*");
317     return 1; 
318   } 
319
320   /*
321    *  Special Case 2: User is on this channel, requesting full names list.
322    *  (As performed with each /join) - ** High frequency usage **
323    */
324
325   clean_channelname(para);
326   chptr = FindChannel(para); 
327
328   if (chptr) {
329     member = find_member_link(chptr, sptr);
330     if (member)
331     { 
332       do_names(sptr, chptr, NAMES_ALL);
333       if (!EmptyString(para))
334       {
335         send_reply(sptr, RPL_ENDOFNAMES, chptr ? chptr->chname : para);
336         return 1;
337       }
338     }
339       else 
340     {
341       /*
342        *  Special Case 3: User isn't on this channel, show all visible users, in 
343        *  non secret channels.
344        */ 
345       do_names(sptr, chptr, NAMES_VIS);
346     } 
347   } else { /* Channel doesn't exist. */ 
348       send_reply(sptr, RPL_ENDOFNAMES, para); 
349   }
350   return 1;
351 }
352
353  
354 /*
355  * ms_names - server message handler
356  *
357  * parv[0] = sender prefix
358  * parv[1] = channel
359  */
360 int ms_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
361 {
362   struct Channel *chptr; 
363   struct Channel *ch2ptr; 
364   struct Client *c2ptr;
365   struct Membership* member; 
366   char* s;
367   char* para = parc > 1 ? parv[1] : 0; 
368
369   if (parc > 2 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %C", 2, parc, parv))
370     return 0; 
371
372   if (EmptyString(para)) {
373     send_reply(sptr, RPL_ENDOFNAMES, "*");
374     return 0;
375   }
376   else if (*para == '0')
377     *para = '\0';
378   
379   s = strchr(para, ','); /* Recursively call m_names for each comma-seperated channel. */
380   if (s) {
381     parv[1] = ++s;
382     m_names(cptr, sptr, parc, parv);
383   }
384  
385   /*
386    * Special Case 1: "/names 0".
387    * Full list as per RFC. 
388    */
389
390   if (!*para) { 
391     int idx; 
392     int mlen;
393     int flag;
394     struct Channel *ch3ptr;
395     char buf[BUFSIZE]; 
396
397     mlen = strlen(cli_name(&me)) + 10 + strlen(cli_name(sptr));
398
399     /* List all visible channels/visible members */ 
400
401     for (ch2ptr = GlobalChannelList; ch2ptr; ch2ptr = ch2ptr->next)
402     { 
403       if (!ShowChannel(sptr, ch2ptr))
404         continue;                 /* Don't show secret chans. */ 
405
406       if (find_channel_member(sptr, ch2ptr))
407       {
408         do_names(sptr, ch2ptr, NAMES_ALL); /* Full list if we're in this chan. */
409       } else { 
410         do_names(sptr, ch2ptr, NAMES_VIS);
411       }
412     } 
413  
414     /* List all remaining users on channel '*' */
415
416     strcpy(buf, "* * :");
417     idx = 5;
418     flag = 0;
419
420     for (c2ptr = GlobalClientList; c2ptr; c2ptr = cli_next(c2ptr))
421     {
422       int showflag = 0;
423
424       if (!IsUser(c2ptr) || (sptr != c2ptr && IsInvisible(c2ptr)))
425         continue;
426
427       member = cli_user(c2ptr)->channel; 
428
429       while (member)
430       {
431         ch3ptr = member->channel;
432   
433         if (PubChannel(ch3ptr) || find_channel_member(sptr, ch3ptr))
434           showflag = 1;
435  
436         member = member->next_channel;
437       }
438
439       if (showflag)               /* Have we already shown them? */
440         continue;
441  
442       strcat(buf, cli_name(c2ptr));
443       strcat(buf, " ");
444       idx += strlen(cli_name(c2ptr)) + 1;
445       flag = 1;
446
447       if (mlen + idx + NICKLEN + 3 > BUFSIZE)     /* space, \r\n\0 */
448       {
449         send_reply(sptr, RPL_NAMREPLY, buf);
450         strcpy(buf, "* * :");
451         idx = 5;
452         flag = 0;
453       }
454     }
455     if (flag)
456       send_reply(sptr, RPL_NAMREPLY, buf);
457     send_reply(sptr, RPL_ENDOFNAMES, "*");
458     return 1; 
459   } 
460
461   /*
462    *  Special Case 2: User is on this channel, requesting full names list.
463    *  (As performed with each /join) - ** High frequency usage **
464    */
465
466   clean_channelname(para);
467   chptr = FindChannel(para); 
468
469   if (chptr) {
470     member = find_member_link(chptr, sptr);
471     if (member)
472     { 
473       do_names(sptr, chptr, NAMES_ALL);
474       if (!EmptyString(para))
475       {
476         send_reply(sptr, RPL_ENDOFNAMES, chptr ? chptr->chname : para);
477         return 1;
478       }
479     }
480       else 
481     {
482       /*
483        *  Special Case 3: User isn't on this channel, show all visible users, in 
484        *  non secret channels.
485        */ 
486       do_names(sptr, chptr, NAMES_VIS);
487     } 
488   } else { /* Channel doesn't exist. */ 
489       send_reply(sptr, RPL_ENDOFNAMES, para); 
490   }
491   return 1;
492 }
493  
494
495 #if 0
496 /*
497  * m_names                              - Added by Jto 27 Apr 1989
498  *
499  * parv[0] = sender prefix
500  * parv[1] = channel
501  */
502 int m_names(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
503 {
504   struct Channel *chptr;
505   struct Client *c2ptr;
506   struct Membership* member;
507   struct Channel *ch2ptr = 0;
508   int idx, flag, len, mlen;
509   char *s, *para = parc > 1 ? parv[1] : 0;
510   char buf[BUFSIZE];
511
512   if (parc > 2 && hunt_server(1, cptr, sptr, "%s%s " TOK_NAMES " %s %s", 2, parc, parv)) /* XXX DEAD */
513     return 0;
514
515   mlen = strlen(me.name) + 10 + strlen(sptr->name);
516
517   if (!EmptyString(para))
518   {
519     s = strchr(para, ',');
520     if (s)
521     {
522       parv[1] = ++s;
523       m_names(cptr, sptr, parc, parv);
524     }
525     clean_channelname(para);
526     ch2ptr = FindChannel(para);
527   }
528
529   /*
530    * First, do all visible channels (public and the one user self is)
531    */
532
533   for (chptr = GlobalChannelList; chptr; chptr = chptr->next)
534   {
535     if ((chptr != ch2ptr) && !EmptyString(para))
536       continue;                 /* -- wanted a specific channel */
537     if (!MyConnect(sptr) && EmptyString(para))
538       continue;
539 #ifndef GODMODE
540     if (!ShowChannel(sptr, chptr))
541       continue;                 /* -- users on this are not listed */
542 #endif
543
544     /* Find users on same channel (defined by chptr) */
545
546     strcpy(buf, "* ");
547     len = strlen(chptr->chname);
548     strcpy(buf + 2, chptr->chname);
549     strcpy(buf + 2 + len, " :");
550
551     if (PubChannel(chptr))
552       *buf = '=';
553     else if (SecretChannel(chptr))
554       *buf = '@';
555     idx = len + 4;
556     flag = 1;
557     for (member = chptr->members; member; member = member->next_member)
558     {
559       c2ptr = member->user;
560 #ifndef GODMODE
561       if (sptr != c2ptr && IsInvisible(c2ptr) && !find_channel_member(sptr, chptr))
562         continue;
563 #endif
564       if (IsZombie(member))
565       {
566         if (member->user != sptr)
567           continue;
568         else
569         {
570           strcat(buf, "!");
571           idx++;
572         }
573       }
574       else if (IsChanOp(member))
575       {
576         strcat(buf, "@");
577         idx++;
578       }
579       else if (HasVoice(member))
580       {
581         strcat(buf, "+");
582         idx++;
583       }
584       strcat(buf, c2ptr->name);
585       strcat(buf, " ");
586       idx += strlen(c2ptr->name) + 1;
587       flag = 1;
588 #ifdef GODMODE
589       {
590         char yxx[6];
591         sprintf_irc(yxx, "%s%s", NumNick(c2ptr));
592         assert(c2ptr == findNUser(yxx));
593         sprintf_irc(buf + strlen(buf), "(%s) ", yxx);
594         idx += 6;
595       }
596       if (mlen + idx + NICKLEN + 11 > BUFSIZE)
597 #else
598       if (mlen + idx + NICKLEN + 5 > BUFSIZE)
599 #endif
600         /* space, modifier, nick, \r \n \0 */
601       {
602         sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf); /* XXX DEAD */
603         strcpy(buf, "* ");
604         ircd_strncpy(buf + 2, chptr->chname, len + 1);
605         buf[len + 2] = 0;
606         strcat(buf, " :");
607         if (PubChannel(chptr))
608           *buf = '=';
609         else if (SecretChannel(chptr))
610           *buf = '@';
611         idx = len + 4;
612         flag = 0;
613       }
614     }
615     if (flag)
616       sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf); /* XXX DEAD */
617   }
618   if (!EmptyString(para))
619   {
620     sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name, parv[0], /* XXX DEAD */
621         ch2ptr ? ch2ptr->chname : para);
622     return (1);
623   }
624
625   /* Second, do all non-public, non-secret channels in one big sweep */
626
627   strcpy(buf, "* * :");
628   idx = 5;
629   flag = 0;
630   for (c2ptr = GlobalClientList; c2ptr; c2ptr = c2ptr->next)
631   {
632     struct Channel *ch3ptr;
633     int showflag = 0, secret = 0;
634
635 #ifndef GODMODE
636     if (!IsUser(c2ptr) || (sptr != c2ptr && IsInvisible(c2ptr)))
637 #else
638     if (!IsUser(c2ptr))
639 #endif
640       continue;
641     member = c2ptr->user->channel;
642     /*
643      * Don't show a client if they are on a secret channel or when
644      * they are on a channel sptr is on since they have already
645      * been show earlier. -avalon
646      */
647     while (member)
648     {
649       ch3ptr = member->channel;
650 #ifndef GODMODE
651       if (PubChannel(ch3ptr) || find_channel_member(sptr, ch3ptr))
652 #endif
653         showflag = 1;
654       if (SecretChannel(ch3ptr))
655         secret = 1;
656       member = member->next_channel;
657     }
658     if (showflag)               /* Have we already shown them ? */
659       continue;
660 #ifndef GODMODE
661     if (secret)                 /* On any secret channels ? */
662       continue;
663 #endif
664     strcat(buf, c2ptr->name);
665     strcat(buf, " ");
666     idx += strlen(c2ptr->name) + 1;
667     flag = 1;
668 #ifdef GODMODE
669     {
670       char yxx[6];
671       sprintf_irc(yxx, "%s%s", NumNick(c2ptr));
672       assert(c2ptr == findNUser(yxx));
673       sprintf_irc(buf + strlen(buf), "(%s) ", yxx);
674       idx += 6;
675     }
676 #endif
677 #ifdef GODMODE
678     if (mlen + idx + NICKLEN + 9 > BUFSIZE)
679 #else
680     if (mlen + idx + NICKLEN + 3 > BUFSIZE)     /* space, \r\n\0 */
681 #endif
682     {
683       sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf); /* XXX DEAD */
684       strcpy(buf, "* * :");
685       idx = 5;
686       flag = 0;
687     }
688   }
689   if (flag)
690     sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf); /* XXX DEAD */
691   sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name, parv[0], "*"); /* XXX DEAD */
692   return 1;
693 }
694 #endif /* 0 */
695