- The big forward port. I probably broke lots of stuff, so please look over any
[ircu2.10.12-pk.git] / ircd / channel.c
1 /*
2  * IRC - Internet Relay Chat, ircd/channel.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Co Center
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 1, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  * $Id$
21  */
22 #include "config.h"
23
24 #include "channel.h"
25 #include "client.h"
26 #include "destruct_event.h"
27 #include "hash.h"
28 #include "ircd.h"
29 #include "ircd_alloc.h"
30 #include "ircd_chattr.h"
31 #include "ircd_defs.h"
32 #include "ircd_features.h"
33 #include "ircd_log.h"
34 #include "ircd_reply.h"
35 #include "ircd_snprintf.h"
36 #include "ircd_string.h"
37 #include "list.h"
38 #include "match.h"
39 #include "msg.h"
40 #include "msgq.h"
41 #include "numeric.h"
42 #include "numnicks.h"
43 #include "querycmds.h"
44 #include "s_bsd.h"
45 #include "s_conf.h"
46 #include "s_debug.h"
47 #include "s_misc.h"
48 #include "s_user.h"
49 #include "send.h"
50 #include "struct.h"
51 #include "support.h"
52 #include "sys.h"
53 #include "whowas.h"
54
55 #include <assert.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59
60 struct Channel* GlobalChannelList = 0;
61
62 static unsigned int membershipAllocCount;
63 static struct Membership* membershipFreeList;
64
65 void del_invite(struct Client *, struct Channel *);
66
67 const char* const PartFmt1     = ":%s " MSG_PART " %s";
68 const char* const PartFmt2     = ":%s " MSG_PART " %s :%s";
69 const char* const PartFmt1serv = "%s%s " TOK_PART " %s";
70 const char* const PartFmt2serv = "%s%s " TOK_PART " %s :%s";
71
72
73 static struct SLink* next_ban;
74 static struct SLink* prev_ban;
75 static struct SLink* removed_bans_list;
76
77 /*
78  * Use a global variable to remember if an oper set a mode on a local channel. Ugly,
79  * but the only way to do it without changing set_mode intensively.
80  */
81 int LocalChanOperMode = 0;
82
83 #if !defined(NDEBUG)
84 /*
85  * return the length (>=0) of a chain of links.
86  */
87 static int list_length(struct SLink *lp)
88 {
89   int count = 0;
90
91   for (; lp; lp = lp->next)
92     ++count;
93   return count;
94 }
95 #endif
96
97 struct Membership* find_member_link(struct Channel* chptr, const struct Client* cptr)
98 {
99   struct Membership *m;
100   assert(0 != cptr);
101   assert(0 != chptr);
102   
103   /* Servers don't have member links */
104   if (IsServer(cptr)||IsMe(cptr))
105      return 0;
106   
107   /* +k users are typically on a LOT of channels.  So we iterate over who
108    * is in the channel.  X/W are +k and are in about 5800 channels each.
109    * however there are typically no more than 1000 people in a channel
110    * at a time.
111    */
112   if (IsChannelService(cptr)) {
113     m = chptr->members;
114     while (m) {
115       assert(m->channel == chptr);
116       if (m->user == cptr)
117         return m;
118       m = m->next_member;
119     }
120   }
121   /* Users on the other hand aren't allowed on more than 15 channels.  50%
122    * of users that are on channels are on 2 or less, 95% are on 7 or less,
123    * and 99% are on 10 or less.
124    */
125   else {
126    m = (cli_user(cptr))->channel;
127    while (m) {
128      assert(m->user == cptr);
129      if (m->channel == chptr)
130        return m;
131      m = m->next_channel;
132    }
133   }
134   return 0;
135 }
136
137 /*
138  * find_chasing - Find the client structure for a nick name (user)
139  * using history mechanism if necessary. If the client is not found, an error
140  * message (NO SUCH NICK) is generated. If the client was found
141  * through the history, chasing will be 1 and otherwise 0.
142  */
143 struct Client* find_chasing(struct Client* sptr, const char* user, int* chasing)
144 {
145   struct Client* who = FindClient(user);
146
147   if (chasing)
148     *chasing = 0;
149   if (who)
150     return who;
151
152   if (!(who = get_history(user, feature_int(FEAT_KILLCHASETIMELIMIT)))) {
153     send_reply(sptr, ERR_NOSUCHNICK, user);
154     return 0;
155   }
156   if (chasing)
157     *chasing = 1;
158   return who;
159 }
160
161 /*
162  * Create a string of form "foo!bar@fubar" given foo, bar and fubar
163  * as the parameters.  If NULL, they become "*".
164  */
165 #define NUH_BUFSIZE     (NICKLEN + USERLEN + HOSTLEN + 3)
166 static char *make_nick_user_host(char *namebuf, const char *nick,
167                                  const char *name, const char *host)
168 {
169   ircd_snprintf(0, namebuf, NUH_BUFSIZE, "%s!%s@%s", nick, name, host);
170   return namebuf;
171 }
172
173 /*
174  * Create a string of form "foo!bar@123.456.789.123" given foo, bar and the
175  * IP-number as the parameters.  If NULL, they become "*".
176  */
177 #define NUI_BUFSIZE     (NICKLEN + USERLEN + 16 + 3)
178 static char *make_nick_user_ip(char *ipbuf, char *nick, char *name,
179                                struct in_addr ip)
180 {
181   ircd_snprintf(0, ipbuf, NUI_BUFSIZE, "%s!%s@%s", nick, name,
182                 ircd_ntoa((const char*) &ip));
183   return ipbuf;
184 }
185
186 /*
187  * Subtract one user from channel i (and free channel
188  * block, if channel became empty).
189  * Returns: true  (1) if channel still has members.
190  *          false (0) if the channel is now empty.
191  */
192 int sub1_from_channel(struct Channel* chptr)
193 {
194   if (chptr->users > 1)         /* Can be 0, called for an empty channel too */
195   {
196     assert(0 != chptr->members);
197     --chptr->users;
198     return 1;
199   }
200
201   chptr->users = 0;
202
203   /*
204    * Also channels without Apass set need to be kept alive,
205    * otherwise Bad Guys(tm) would be able to takeover
206    * existing channels too easily, and then set an Apass!
207    * However, if a channel without Apass becomes empty
208    * then we try to be kind to them and remove possible
209    * limiting modes.
210    */
211   chptr->mode.mode &= ~MODE_INVITEONLY;
212   chptr->mode.limit = 0;
213   /*
214    * We do NOT reset a possible key or bans because when
215    * the 'channel owners' can't get in because of a key
216    * or ban then apparently there was a fight/takeover
217    * on the channel and we want them to contact IRC opers
218    * who then will educate them on the use of Apass/upass.
219    */
220
221   if (TStime() - chptr->creationtime < 172800)  /* Channel younger than 48 hours? */
222     schedule_destruct_event_1m(chptr);          /* Get rid of it in approximately 4-5 minutes */
223   else
224     schedule_destruct_event_48h(chptr);         /* Get rid of it in approximately 48 hours */
225
226   return 0;
227 }
228
229 int destruct_channel(struct Channel* chptr)
230 {
231   struct SLink *tmp;
232   struct SLink *obtmp;
233
234   assert(0 == chptr->members);
235
236   /* Channel became (or was) empty: Remove channel */
237   if (is_listed(chptr))
238   {
239     int i;
240     for (i = 0; i <= HighestFd; i++)
241     {
242       struct Client *acptr = 0;
243       if ((acptr = LocalClientArray[i]) && cli_listing(acptr) &&
244           (cli_listing(acptr))->chptr == chptr)
245       {
246         list_next_channels(acptr, 1);
247         break;                  /* Only one client can list a channel */
248       }
249     }
250   }
251   /*
252    * Now, find all invite links from channel structure
253    */
254   while ((tmp = chptr->invites))
255     del_invite(tmp->value.cptr, chptr);
256
257   tmp = chptr->banlist;
258   while (tmp)
259   {
260     obtmp = tmp;
261     tmp = tmp->next;
262     MyFree(obtmp->value.ban.banstr);
263     MyFree(obtmp->value.ban.who);
264     free_link(obtmp);
265   }
266   if (chptr->prev)
267     chptr->prev->next = chptr->next;
268   else
269     GlobalChannelList = chptr->next;
270   if (chptr->next)
271     chptr->next->prev = chptr->prev;
272   hRemChannel(chptr);
273   --UserStats.channels;
274   /*
275    * make sure that channel actually got removed from hash table
276    */
277   assert(chptr->hnext == chptr);
278   MyFree(chptr);
279   return 0;
280 }
281
282 /*
283  * add_banid
284  *
285  * `cptr' must be the client adding the ban.
286  *
287  * If `change' is true then add `banid' to channel `chptr'.
288  * Returns 0 if the ban was added.
289  * Returns -2 if the ban already existed and was marked CHFL_BURST_BAN_WIPEOUT.
290  * Return -1 otherwise.
291  *
292  * Those bans that overlapped with `banid' are flagged with CHFL_BAN_OVERLAPPED
293  * when `change' is false, otherwise they will be removed from the banlist.
294  * Subsequently calls to next_overlapped_ban() or next_removed_overlapped_ban()
295  * respectively will return these bans until NULL is returned.
296  *
297  * If `firsttime' is true, the ban list as returned by next_overlapped_ban()
298  * is reset (unless a non-zero value is returned, in which case the
299  * CHFL_BAN_OVERLAPPED flag might not have been reset!).
300  *
301  * --Run
302  */
303 int add_banid(struct Client *cptr, struct Channel *chptr, char *banid,
304                      int change, int firsttime)
305 {
306   struct SLink*  ban;
307   struct SLink** banp;
308   int            cnt = 0;
309   int            removed_bans = 0;
310   int            len = strlen(banid);
311
312   if (firsttime)
313   {
314     next_ban = NULL;
315     assert(0 == prev_ban);
316     assert(0 == removed_bans_list);
317   }
318   if (MyUser(cptr))
319     collapse(banid);
320   for (banp = &chptr->banlist; *banp;)
321   {
322     len += strlen((*banp)->value.ban.banstr);
323     ++cnt;
324     if (((*banp)->flags & CHFL_BURST_BAN_WIPEOUT))
325     {
326       if (!strcmp((*banp)->value.ban.banstr, banid))
327       {
328         (*banp)->flags &= ~CHFL_BURST_BAN_WIPEOUT;
329         return -2;
330       }
331     }
332     else if (!mmatch((*banp)->value.ban.banstr, banid))
333       return -1;
334     if (!mmatch(banid, (*banp)->value.ban.banstr))
335     {
336       struct SLink *tmp = *banp;
337       if (change)
338       {
339         if (MyUser(cptr))
340         {
341           cnt--;
342           len -= strlen(tmp->value.ban.banstr);
343         }
344         *banp = tmp->next;
345         /* These will be sent to the user later as -b */
346         tmp->next = removed_bans_list;
347         removed_bans_list = tmp;
348         removed_bans = 1;
349       }
350       else if (!(tmp->flags & CHFL_BURST_BAN_WIPEOUT))
351       {
352         tmp->flags |= CHFL_BAN_OVERLAPPED;
353         if (!next_ban)
354           next_ban = tmp;
355         banp = &tmp->next;
356       }
357       else
358         banp = &tmp->next;
359     }
360     else
361     {
362       if (firsttime)
363         (*banp)->flags &= ~CHFL_BAN_OVERLAPPED;
364       banp = &(*banp)->next;
365     }
366   }
367   if (MyUser(cptr) && !removed_bans &&
368       (len > (feature_int(FEAT_AVBANLEN) * feature_int(FEAT_MAXBANS)) ||
369        (cnt >= feature_int(FEAT_MAXBANS))))
370   {
371     send_reply(cptr, ERR_BANLISTFULL, chptr->chname, banid);
372     return -1;
373   }
374   if (change)
375   {
376     char*              ip_start;
377     struct Membership* member;
378     ban = make_link();
379     ban->next = chptr->banlist;
380
381     ban->value.ban.banstr = (char*) MyMalloc(strlen(banid) + 1);
382     assert(0 != ban->value.ban.banstr);
383     strcpy(ban->value.ban.banstr, banid);
384
385     if (IsServer(cptr) && feature_bool(FEAT_HIS_BANWHO))
386       DupString(ban->value.ban.who, cli_name(&me));
387     else
388       DupString(ban->value.ban.who, cli_name(cptr));
389     assert(0 != ban->value.ban.who);
390
391     ban->value.ban.when = TStime();
392     ban->flags = CHFL_BAN;      /* This bit is never used I think... */
393     if ((ip_start = strrchr(banid, '@')) && check_if_ipmask(ip_start + 1))
394       ban->flags |= CHFL_BAN_IPMASK;
395     chptr->banlist = ban;
396
397     /*
398      * Erase ban-valid-bit
399      */
400     for (member = chptr->members; member; member = member->next_member)
401       ClearBanValid(member);     /* `ban' == channel member ! */
402   }
403   return 0;
404 }
405
406 struct SLink *next_removed_overlapped_ban(void)
407 {
408   struct SLink *tmp = removed_bans_list;
409   if (prev_ban)
410   {
411     if (prev_ban->value.ban.banstr)     /* Can be set to NULL in set_mode() */
412       MyFree(prev_ban->value.ban.banstr);
413     MyFree(prev_ban->value.ban.who);
414     free_link(prev_ban);
415     prev_ban = 0;
416   }
417   if (tmp)
418     removed_bans_list = removed_bans_list->next;
419   prev_ban = tmp;
420   return tmp;
421 }
422
423 /*
424  * find_channel_member - returns Membership * if a person is joined and not a zombie
425  */
426 struct Membership* find_channel_member(struct Client* cptr, struct Channel* chptr)
427 {
428   struct Membership* member;
429   assert(0 != chptr);
430
431   member = find_member_link(chptr, cptr);
432   return (member && !IsZombie(member)) ? member : 0;
433 }
434
435 /*
436  * is_banned - a non-zero value if banned else 0.
437  */
438 static int is_banned(struct Client *cptr, struct Channel *chptr,
439                      struct Membership* member)
440 {
441   struct SLink* tmp;
442   char          tmphost[HOSTLEN + 1];
443   char          nu_host[NUH_BUFSIZE];
444   char          nu_realhost[NUH_BUFSIZE];
445   char          nu_ip[NUI_BUFSIZE];
446   char*         s;
447   char*         sr = NULL;
448   char*         ip_s = NULL;
449
450   if (!IsUser(cptr))
451     return 0;
452
453   if (member && IsBanValid(member))
454     return IsBanned(member);
455
456   s = make_nick_user_host(nu_host, cli_name(cptr), (cli_user(cptr))->username,
457                           (cli_user(cptr))->host);
458   if (IsAccount(cptr))
459   {
460     if (HasHiddenHost(cptr))
461     {
462       sr = make_nick_user_host(nu_realhost, cli_name(cptr),
463                                (cli_user(cptr))->username,
464                                cli_user(cptr)->realhost);
465     }
466     else
467     {
468       ircd_snprintf(0, tmphost, HOSTLEN, "%s.%s",
469                     cli_user(cptr)->account, feature_str(FEAT_HIDDEN_HOST));
470       sr = make_nick_user_host(nu_realhost, cli_name(cptr),
471                                cli_user(cptr)->username,
472                                tmphost);      
473     }
474   }
475
476   for (tmp = chptr->banlist; tmp; tmp = tmp->next) {
477     if ((tmp->flags & CHFL_BAN_IPMASK)) {
478       if (!ip_s)
479         ip_s = make_nick_user_ip(nu_ip, cli_name(cptr),
480                                  (cli_user(cptr))->username, cli_ip(cptr));
481       if (match(tmp->value.ban.banstr, ip_s) == 0)
482         break;
483     }
484     else if (match(tmp->value.ban.banstr, s) == 0)
485       break;
486     else if (sr && match(tmp->value.ban.banstr, sr) == 0)
487       break;
488   }
489
490   if (member) {
491     SetBanValid(member);
492     if (tmp) {
493       SetBanned(member);
494       return 1;
495     }
496     else {
497       ClearBanned(member);
498       return 0;
499     }
500   }
501
502   return (tmp != NULL);
503 }
504
505 /*
506  * adds a user to a channel by adding another link to the channels member
507  * chain.
508  */
509 void add_user_to_channel(struct Channel* chptr, struct Client* who,
510                                 unsigned int flags, int oplevel)
511 {
512   assert(0 != chptr);
513   assert(0 != who);
514
515   if (cli_user(who)) {
516    
517     struct Membership* member = membershipFreeList;
518     if (member)
519       membershipFreeList = member->next_member;
520     else {
521       member = (struct Membership*) MyMalloc(sizeof(struct Membership));
522       ++membershipAllocCount;
523     }
524
525     assert(0 != member);
526     member->user         = who;
527     member->channel      = chptr;
528     member->status       = flags;
529     member->oplevel      = oplevel;
530
531     member->next_member  = chptr->members;
532     if (member->next_member)
533       member->next_member->prev_member = member;
534     member->prev_member  = 0; 
535     chptr->members       = member;
536
537     member->next_channel = (cli_user(who))->channel;
538     if (member->next_channel)
539       member->next_channel->prev_channel = member;
540     member->prev_channel = 0;
541     (cli_user(who))->channel = member;
542
543     if (chptr->destruct_event)
544       remove_destruct_event(chptr);
545     ++chptr->users;
546     ++((cli_user(who))->joined);
547   }
548 }
549
550 static int remove_member_from_channel(struct Membership* member)
551 {
552   struct Channel* chptr;
553   assert(0 != member);
554   chptr = member->channel;
555   /*
556    * unlink channel member list
557    */
558   if (member->next_member)
559     member->next_member->prev_member = member->prev_member;
560   if (member->prev_member)
561     member->prev_member->next_member = member->next_member;
562   else
563     member->channel->members = member->next_member; 
564       
565   /*
566    * unlink client channel list
567    */
568   if (member->next_channel)
569     member->next_channel->prev_channel = member->prev_channel;
570   if (member->prev_channel)
571     member->prev_channel->next_channel = member->next_channel;
572   else
573     (cli_user(member->user))->channel = member->next_channel;
574
575   --(cli_user(member->user))->joined;
576
577   member->next_member = membershipFreeList;
578   membershipFreeList = member;
579
580   return sub1_from_channel(chptr);
581 }
582
583 static int channel_all_zombies(struct Channel* chptr)
584 {
585   struct Membership* member;
586
587   for (member = chptr->members; member; member = member->next_member) {
588     if (!IsZombie(member))
589       return 0;
590   }
591   return 1;
592 }
593       
594
595 void remove_user_from_channel(struct Client* cptr, struct Channel* chptr)
596 {
597   
598   struct Membership* member;
599   assert(0 != chptr);
600
601   if ((member = find_member_link(chptr, cptr))) {
602     if (remove_member_from_channel(member)) {
603       if (channel_all_zombies(chptr)) {
604         /*
605          * XXX - this looks dangerous but isn't if we got the referential
606          * integrity right for channels
607          */
608         while (remove_member_from_channel(chptr->members))
609           ;
610       }
611     }
612   }
613 }
614
615 void remove_user_from_all_channels(struct Client* cptr)
616 {
617   struct Membership* chan;
618   assert(0 != cptr);
619   assert(0 != cli_user(cptr));
620
621   while ((chan = (cli_user(cptr))->channel))
622     remove_user_from_channel(cptr, chan->channel);
623 }
624
625 int is_chan_op(struct Client *cptr, struct Channel *chptr)
626 {
627   struct Membership* member;
628   assert(chptr);
629   if ((member = find_member_link(chptr, cptr)))
630     return (!IsZombie(member) && IsChanOp(member));
631
632   return 0;
633 }
634
635 int is_zombie(struct Client *cptr, struct Channel *chptr)
636 {
637   struct Membership* member;
638
639   assert(0 != chptr);
640
641   if ((member = find_member_link(chptr, cptr)))
642       return IsZombie(member);
643   return 0;
644 }
645
646 int has_voice(struct Client* cptr, struct Channel* chptr)
647 {
648   struct Membership* member;
649
650   assert(0 != chptr);
651   if ((member = find_member_link(chptr, cptr)))
652     return (!IsZombie(member) && HasVoice(member));
653
654   return 0;
655 }
656
657 int member_can_send_to_channel(struct Membership* member)
658 {
659   assert(0 != member);
660
661   /* Discourage using the Apass to get op.  They should use the upass. */
662   if (IsChannelManager(member) && *member->channel->mode.upass)
663     return 0;
664
665   if (IsVoicedOrOpped(member))
666     return 1;
667   /*
668    * If it's moderated, and you aren't a priviledged user, you can't
669    * speak.  
670    */
671   if (member->channel->mode.mode & MODE_MODERATED)
672     return 0;
673   /*
674    * If you're banned then you can't speak either.
675    * but because of the amount of CPU time that is_banned chews
676    * we only check it for our clients.
677    */
678   if (MyUser(member->user) && is_banned(member->user, member->channel, member))
679     return 0;
680   return 1;
681 }
682
683 int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr)
684 {
685   struct Membership *member;
686   assert(0 != cptr); 
687   /*
688    * Servers can always speak on channels.
689    */
690   if (IsServer(cptr))
691     return 1;
692
693   member = find_channel_member(cptr, chptr);
694
695   /*
696    * You can't speak if you're off channel, and it is +n (no external messages)
697    * or +m (moderated).
698    */
699   if (!member) {
700     if ((chptr->mode.mode & (MODE_NOPRIVMSGS|MODE_MODERATED)) ||
701         ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(cptr)))
702       return 0;
703     else
704       return !is_banned(cptr, chptr, NULL);
705   }
706   return member_can_send_to_channel(member); 
707 }
708
709 /*
710  * find_no_nickchange_channel
711  * if a member and not opped or voiced and banned
712  * return the name of the first channel banned on
713  */
714 const char* find_no_nickchange_channel(struct Client* cptr)
715 {
716   if (MyUser(cptr)) {
717     struct Membership* member;
718     for (member = (cli_user(cptr))->channel; member;
719          member = member->next_channel) {
720       if (!IsVoicedOrOpped(member) && is_banned(cptr, member->channel, member))
721         return member->channel->chname;
722     }
723   }
724   return 0;
725 }
726
727
728 /*
729  * write the "simple" list of channel modes for channel chptr onto buffer mbuf
730  * with the parameters in pbuf.
731  */
732 void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
733                           struct Channel *chptr, struct Membership *member)
734 {
735   int previous_parameter = 0;
736
737   assert(0 != mbuf);
738   assert(0 != pbuf);
739   assert(0 != chptr);
740
741   *mbuf++ = '+';
742   if (chptr->mode.mode & MODE_SECRET)
743     *mbuf++ = 's';
744   else if (chptr->mode.mode & MODE_PRIVATE)
745     *mbuf++ = 'p';
746   if (chptr->mode.mode & MODE_MODERATED)
747     *mbuf++ = 'm';
748   if (chptr->mode.mode & MODE_TOPICLIMIT)
749     *mbuf++ = 't';
750   if (chptr->mode.mode & MODE_INVITEONLY)
751     *mbuf++ = 'i';
752   if (chptr->mode.mode & MODE_NOPRIVMSGS)
753     *mbuf++ = 'n';
754   if (chptr->mode.mode & MODE_REGONLY)
755     *mbuf++ = 'r';
756   if (chptr->mode.limit) {
757     *mbuf++ = 'l';
758     ircd_snprintf(0, pbuf, buflen, "%u", chptr->mode.limit);
759     previous_parameter = 1;
760   }
761
762   if (*chptr->mode.key) {
763     *mbuf++ = 'k';
764     if (previous_parameter)
765       strcat(pbuf, " ");
766     if (is_chan_op(cptr, chptr) || IsServer(cptr)) {
767       strcat(pbuf, chptr->mode.key);
768     } else
769       strcat(pbuf, "*");
770     previous_parameter = 1;
771   }
772   if (*chptr->mode.apass) {
773     *mbuf++ = 'A';
774     if (previous_parameter)
775       strcat(pbuf, " ");
776     if (IsServer(cptr)) {
777       strcat(pbuf, chptr->mode.apass);
778     } else
779       strcat(pbuf, "*");
780     previous_parameter = 1;
781   }
782   if (*chptr->mode.upass) {
783     *mbuf++ = 'u';
784     if (previous_parameter)
785       strcat(pbuf, " ");
786     if (IsServer(cptr) || (member && IsChanOp(member) && OpLevel(member) == 0)) {
787       strcat(pbuf, chptr->mode.upass);
788     } else
789       strcat(pbuf, "*");
790   }
791   *mbuf = '\0';
792 }
793
794 int compare_member_oplevel(const void *mp1, const void *mp2)
795 {
796   struct Membership const* member1 = *(struct Membership const**)mp1;
797   struct Membership const* member2 = *(struct Membership const**)mp2;
798   if (member1->oplevel == member2->oplevel)
799     return 0;
800   return (member1->oplevel < member2->oplevel) ? -1 : 1;
801 }
802
803 /*
804  * send "cptr" a full list of the modes for channel chptr.
805  */
806 void send_channel_modes(struct Client *cptr, struct Channel *chptr)
807 {
808   /* The order in which modes are generated is now mandatory */
809   static unsigned int current_flags[4] =
810       { 0, CHFL_VOICE, CHFL_CHANOP, CHFL_CHANOP | CHFL_VOICE };
811   int                first = 1;
812   int                full  = 1;
813   int                flag_cnt = 0;
814   int                new_mode = 0;
815   size_t             len;
816   struct Membership* member;
817   struct SLink*      lp2;
818   char modebuf[MODEBUFLEN];
819   char parabuf[MODEBUFLEN];
820   struct MsgBuf *mb;
821   int                 number_of_ops = 0;
822   int                 opped_members_index = 0;
823   struct Membership** opped_members = NULL;
824   int                 last_oplevel = 0;
825
826   assert(0 != cptr);
827   assert(0 != chptr); 
828
829   if (IsLocalChannel(chptr->chname))
830     return;
831
832   member = chptr->members;
833   lp2 = chptr->banlist;
834
835   *modebuf = *parabuf = '\0';
836   channel_modes(cptr, modebuf, parabuf, sizeof(parabuf), chptr, 0);
837
838   for (first = 1; full; first = 0)      /* Loop for multiple messages */
839   {
840     full = 0;                   /* Assume by default we get it
841                                  all in one message */
842
843     /* (Continued) prefix: "<Y> B <channel> <TS>" */
844     /* is there any better way we can do this? */
845     mb = msgq_make(&me, "%C " TOK_BURST " %H %Tu", &me, chptr,
846                    chptr->creationtime);
847
848     if (first && modebuf[1])    /* Add simple modes (Aiklmnpstu)
849                                  if first message */
850     {
851       /* prefix: "<Y> B <channel> <TS>[ <modes>[ <params>]]" */
852       msgq_append(&me, mb, " %s", modebuf);
853
854       if (*parabuf)
855         msgq_append(&me, mb, " %s", parabuf);
856     }
857
858     /*
859      * Attach nicks, comma seperated " nick[:modes],nick[:modes],..."
860      *
861      * First find all opless members.
862      * Run 2 times over all members, to group the members with
863      * and without voice together.
864      * Then run 2 times over all opped members (which are ordered
865      * by op-level) to also group voice and non-voice together.
866      */
867     for (first = 1; flag_cnt < 4; new_mode = 1, ++flag_cnt)
868     {
869       while (member)
870       {
871         if (flag_cnt < 2 && IsChanOp(member))
872         {
873           /*
874            * The first loop (to find all non-voice/op), we count the ops.
875            * The second loop (to find all voiced non-ops), store the ops
876            * in a dynamic array.
877            */
878           if (flag_cnt == 0)
879             ++number_of_ops;
880           else
881             opped_members[opped_members_index++] = member;
882         }
883         /* Only handle the members with the flags that we are interested in. */
884         if ((member->status & CHFL_VOICED_OR_OPPED) == current_flags[flag_cnt])
885         {
886           if (msgq_bufleft(mb) < NUMNICKLEN + 3 + MAXOPLEVELDIGITS)
887             /* The 3 + MAXOPLEVELDIGITS is a possible ",:v999". */
888           {
889             full = 1;           /* Make sure we continue after
890                                    sending it so far */
891             /* Ensure the new BURST line contains the current
892              * ":mode", except when there is no mode yet. */
893             new_mode = (flag_cnt > 0) ? 1 : 0;
894             break;              /* Do not add this member to this message */
895           }
896           msgq_append(&me, mb, "%c%C", first ? ' ' : ',', member->user);
897           first = 0;              /* From now on, use commas to add new nicks */
898
899           /*
900            * Do we have a nick with a new mode ?
901            * Or are we starting a new BURST line?
902            */
903           if (new_mode)
904           {
905             /*
906              * This means we are at the _first_ member that has only
907              * voice, or the first member that has only ops, or the
908              * first member that has voice and ops (so we get here
909              * at most three times, plus once for every start of
910              * a continued BURST line where only these modes is current.
911              * In the two cases where the current mode includes ops,
912              * we need to add the _absolute_ value of the oplevel to the mode.
913              */
914             char tbuf[3 + MAXOPLEVELDIGITS] = ":";
915             int loc = 1;
916
917             if (HasVoice(member))       /* flag_cnt == 1 or 3 */
918               tbuf[loc++] = 'v';
919             if (IsChanOp(member))       /* flag_cnt == 2 or 3 */
920             {
921               /* append the absolute value of the oplevel */
922               loc += ircd_snprintf(0, tbuf + loc, sizeof(tbuf) - loc, "%u", member->oplevel);
923               last_oplevel = member->oplevel;
924             }
925             tbuf[loc] = '\0';
926             msgq_append(&me, mb, tbuf);
927             new_mode = 0;
928           }
929           else if (flag_cnt > 1 && last_oplevel != member->oplevel)
930           {
931             /*
932              * This can't be the first member of a (continued) BURST
933              * message because then either flag_cnt == 0 or new_mode == 1
934              * Now we need to append the incremental value of the oplevel.
935              */
936             char tbuf[2 + MAXOPLEVELDIGITS];
937             ircd_snprintf(0, tbuf, sizeof(tbuf), ":%u", member->oplevel - last_oplevel);
938             last_oplevel = member->oplevel;
939             msgq_append(&me, mb, tbuf);
940           }
941         }
942         /* Go to the next `member'. */
943         if (flag_cnt < 2)
944           member = member->next_member;
945         else
946           member = opped_members[++opped_members_index];
947       }
948       if (full)
949         break;
950
951       /* Point `member' at the start of the list again. */
952       if (flag_cnt == 0)
953       {
954         member = chptr->members;
955         /* Now, after one loop, we know the number of ops and can
956          * allocate the dynamic array with pointer to the ops. */
957         opped_members = (struct Membership**)
958           MyMalloc((number_of_ops + 1) * sizeof(struct Membership*));
959         opped_members[number_of_ops] = NULL;    /* Needed for loop termination */
960       }
961       else
962       {
963         /* At the end of the second loop, sort the opped members with
964          * increasing op-level, so that we will output them in the
965          * correct order (and all op-level increments stay positive) */
966         if (flag_cnt == 1)
967           qsort(opped_members, number_of_ops,
968                 sizeof(struct Membership*), compare_member_oplevel);
969         /* The third and fourth loop run only over the opped members. */
970         member = opped_members[(opped_members_index = 0)];
971       }
972
973     } /* loop over 0,+v,+o,+ov */
974
975     if (!full)
976     {
977       /* Attach all bans, space seperated " :%ban ban ..." */
978       for (first = 2; lp2; lp2 = lp2->next)
979       {
980         len = strlen(lp2->value.ban.banstr);
981         if (msgq_bufleft(mb) < len + 1 + first)
982           /* The +1 stands for the added ' '.
983            * The +first stands for the added ":%".
984            */
985         {
986           full = 1;
987           break;
988         }
989         msgq_append(&me, mb, " %s%s", first ? ":%" : "",
990                     lp2->value.ban.banstr);
991         first = 0;
992       }
993     }
994
995     send_buffer(cptr, mb, 0);  /* Send this message */
996     msgq_clean(mb);
997   }                             /* Continue when there was something
998                                  that didn't fit (full==1) */
999   if (opped_members)
1000     MyFree(opped_members);
1001 }
1002
1003 /*
1004  * pretty_mask
1005  *
1006  * by Carlo Wood (Run), 05 Oct 1998.
1007  *
1008  * Canonify a mask.
1009  *
1010  * When the nick is longer then NICKLEN, it is cut off (its an error of course).
1011  * When the user name or host name are too long (USERLEN and HOSTLEN
1012  * respectively) then they are cut off at the start with a '*'.
1013  *
1014  * The following transformations are made:
1015  *
1016  * 1)   xxx             -> nick!*@*
1017  * 2)   xxx.xxx         -> *!*@host
1018  * 3)   xxx!yyy         -> nick!user@*
1019  * 4)   xxx@yyy         -> *!user@host
1020  * 5)   xxx!yyy@zzz     -> nick!user@host
1021  */
1022 char *pretty_mask(char *mask)
1023 {
1024   static char star[2] = { '*', 0 };
1025   static char retmask[NUH_BUFSIZE];
1026   char *last_dot = NULL;
1027   char *ptr;
1028
1029   /* Case 1: default */
1030   char *nick = mask;
1031   char *user = star;
1032   char *host = star;
1033
1034   /* Do a _single_ pass through the characters of the mask: */
1035   for (ptr = mask; *ptr; ++ptr)
1036   {
1037     if (*ptr == '!')
1038     {
1039       /* Case 3 or 5: Found first '!' (without finding a '@' yet) */
1040       user = ++ptr;
1041       host = star;
1042     }
1043     else if (*ptr == '@')
1044     {
1045       /* Case 4: Found last '@' (without finding a '!' yet) */
1046       nick = star;
1047       user = mask;
1048       host = ++ptr;
1049     }
1050     else if (*ptr == '.')
1051     {
1052       /* Case 2: Found last '.' (without finding a '!' or '@' yet) */
1053       last_dot = ptr;
1054       continue;
1055     }
1056     else
1057       continue;
1058     for (; *ptr; ++ptr)
1059     {
1060       if (*ptr == '@')
1061       {
1062         /* Case 4 or 5: Found last '@' */
1063         host = ptr + 1;
1064       }
1065     }
1066     break;
1067   }
1068   if (user == star && last_dot)
1069   {
1070     /* Case 2: */
1071     nick = star;
1072     user = star;
1073     host = mask;
1074   }
1075   /* Check lengths */
1076   if (nick != star)
1077   {
1078     char *nick_end = (user != star) ? user - 1 : ptr;
1079     if (nick_end - nick > NICKLEN)
1080       nick[NICKLEN] = 0;
1081     *nick_end = 0;
1082   }
1083   if (user != star)
1084   {
1085     char *user_end = (host != star) ? host - 1 : ptr;
1086     if (user_end - user > USERLEN)
1087     {
1088       user = user_end - USERLEN;
1089       *user = '*';
1090     }
1091     *user_end = 0;
1092   }
1093   if (host != star && ptr - host > HOSTLEN)
1094   {
1095     host = ptr - HOSTLEN;
1096     *host = '*';
1097   }
1098   return make_nick_user_host(retmask, nick, user, host);
1099 }
1100
1101 static void send_ban_list(struct Client* cptr, struct Channel* chptr)
1102 {
1103   struct SLink* lp;
1104
1105   assert(0 != cptr);
1106   assert(0 != chptr);
1107
1108   for (lp = chptr->banlist; lp; lp = lp->next)
1109     send_reply(cptr, RPL_BANLIST, chptr->chname, lp->value.ban.banstr,
1110                lp->value.ban.who, lp->value.ban.when);
1111
1112   send_reply(cptr, RPL_ENDOFBANLIST, chptr->chname);
1113 }
1114
1115 /* We are now treating the <key> part of /join <channel list> <key> as a key
1116  * ring; that is, we try one key against the actual channel key, and if that
1117  * doesn't work, we try the next one, and so on. -Kev -Texaco
1118  * Returns: 0 on match, 1 otherwise
1119  * This version contributed by SeKs <intru@info.polymtl.ca>
1120  */
1121 static int compall(char *key, char *keyring)
1122 {
1123   char *p1;
1124
1125 top:
1126   p1 = key;                     /* point to the key... */
1127   while (*p1 && *p1 == *keyring)
1128   {                             /* step through the key and ring until they
1129                                    don't match... */
1130     p1++;
1131     keyring++;
1132   }
1133
1134   if (!*p1 && (!*keyring || *keyring == ','))
1135     /* ok, if we're at the end of the and also at the end of one of the keys
1136        in the keyring, we have a match */
1137     return 0;
1138
1139   if (!*keyring)                /* if we're at the end of the key ring, there
1140                                    weren't any matches, so we return 1 */
1141     return 1;
1142
1143   /* Not at the end of the key ring, so step
1144      through to the next key in the ring: */
1145   while (*keyring && *(keyring++) != ',');
1146
1147   goto top;                     /* and check it against the key */
1148 }
1149
1150 int can_join(struct Client *sptr, struct Channel *chptr, char *key)
1151 {
1152   struct SLink *lp;
1153   int overrideJoin = 0;  
1154   
1155   /*
1156    * Now a banned user CAN join if invited -- Nemesi
1157    * Now a user CAN escape channel limit if invited -- bfriendly
1158    * Now a user CAN escape anything if invited -- Isomer
1159    */
1160
1161   for (lp = (cli_user(sptr))->invited; lp; lp = lp->next)
1162     if (lp->value.chptr == chptr)
1163       return 0;
1164   
1165   /* An oper can force a join on a local channel using "OVERRIDE" as the key. 
1166      a HACK(4) notice will be sent if he would not have been supposed
1167      to join normally. */ 
1168   if (IsLocalChannel(chptr->chname) && HasPriv(sptr, PRIV_WALK_LCHAN) &&
1169       !BadPtr(key) && compall("OVERRIDE",chptr->mode.key) != 0 &&
1170       compall("OVERRIDE",key) == 0)
1171     overrideJoin = MAGIC_OPER_OVERRIDE;
1172
1173   if (chptr->mode.mode & MODE_INVITEONLY)
1174         return overrideJoin + ERR_INVITEONLYCHAN;
1175         
1176   if (chptr->mode.limit && chptr->users >= chptr->mode.limit)
1177         return overrideJoin + ERR_CHANNELISFULL;
1178
1179   if ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(sptr))
1180         return overrideJoin + ERR_NEEDREGGEDNICK;
1181         
1182   if (is_banned(sptr, chptr, NULL))
1183         return overrideJoin + ERR_BANNEDFROMCHAN;
1184   
1185   /*
1186    * now using compall (above) to test against a whole key ring -Kev
1187    */
1188   if (*chptr->mode.key && (EmptyString(key) || compall(chptr->mode.key, key)))
1189     return overrideJoin + ERR_BADCHANNELKEY;
1190
1191   if (overrideJoin)     
1192         return ERR_DONTCHEAT;
1193         
1194   return 0;
1195 }
1196
1197 /*
1198  * Remove bells and commas from channel name
1199  */
1200 void clean_channelname(char *cn)
1201 {
1202   int i;
1203
1204   for (i = 0; cn[i]; i++) {
1205     if (i >= CHANNELLEN || !IsChannelChar(cn[i])) {
1206       cn[i] = '\0';
1207       return;
1208     }
1209     if (IsChannelLower(cn[i])) {
1210       cn[i] = ToLower(cn[i]);
1211 #ifndef FIXME
1212       /*
1213        * Remove for .08+
1214        * toupper(0xd0)
1215        */
1216       if ((unsigned char)(cn[i]) == 0xd0)
1217         cn[i] = (char) 0xf0;
1218 #endif
1219     }
1220   }
1221 }
1222
1223 /*
1224  *  Get Channel block for i (and allocate a new channel
1225  *  block, if it didn't exists before).
1226  */
1227 struct Channel *get_channel(struct Client *cptr, char *chname, ChannelGetType flag)
1228 {
1229   struct Channel *chptr;
1230   int len;
1231
1232   if (EmptyString(chname))
1233     return NULL;
1234
1235   len = strlen(chname);
1236   if (MyUser(cptr) && len > CHANNELLEN)
1237   {
1238     len = CHANNELLEN;
1239     *(chname + CHANNELLEN) = '\0';
1240   }
1241   if ((chptr = FindChannel(chname)))
1242     return (chptr);
1243   if (flag == CGT_CREATE)
1244   {
1245     chptr = (struct Channel*) MyMalloc(sizeof(struct Channel) + len);
1246     assert(0 != chptr);
1247     ++UserStats.channels;
1248     memset(chptr, 0, sizeof(struct Channel));
1249     strcpy(chptr->chname, chname);
1250     if (GlobalChannelList)
1251       GlobalChannelList->prev = chptr;
1252     chptr->prev = NULL;
1253     chptr->next = GlobalChannelList;
1254     chptr->creationtime = MyUser(cptr) ? TStime() : (time_t) 0;
1255     GlobalChannelList = chptr;
1256     hAddChannel(chptr);
1257   }
1258   return chptr;
1259 }
1260
1261 void add_invite(struct Client *cptr, struct Channel *chptr)
1262 {
1263   struct SLink *inv, **tmp;
1264
1265   del_invite(cptr, chptr);
1266   /*
1267    * Delete last link in chain if the list is max length
1268    */
1269   assert(list_length((cli_user(cptr))->invited) == (cli_user(cptr))->invites);
1270   if ((cli_user(cptr))->invites >= feature_int(FEAT_MAXCHANNELSPERUSER))
1271     del_invite(cptr, (cli_user(cptr))->invited->value.chptr);
1272   /*
1273    * Add client to channel invite list
1274    */
1275   inv = make_link();
1276   inv->value.cptr = cptr;
1277   inv->next = chptr->invites;
1278   chptr->invites = inv;
1279   /*
1280    * Add channel to the end of the client invite list
1281    */
1282   for (tmp = &((cli_user(cptr))->invited); *tmp; tmp = &((*tmp)->next));
1283   inv = make_link();
1284   inv->value.chptr = chptr;
1285   inv->next = NULL;
1286   (*tmp) = inv;
1287   (cli_user(cptr))->invites++;
1288 }
1289
1290 /*
1291  * Delete Invite block from channel invite list and client invite list
1292  */
1293 void del_invite(struct Client *cptr, struct Channel *chptr)
1294 {
1295   struct SLink **inv, *tmp;
1296
1297   for (inv = &(chptr->invites); (tmp = *inv); inv = &tmp->next)
1298     if (tmp->value.cptr == cptr)
1299     {
1300       *inv = tmp->next;
1301       free_link(tmp);
1302       tmp = 0;
1303       (cli_user(cptr))->invites--;
1304       break;
1305     }
1306
1307   for (inv = &((cli_user(cptr))->invited); (tmp = *inv); inv = &tmp->next)
1308     if (tmp->value.chptr == chptr)
1309     {
1310       *inv = tmp->next;
1311       free_link(tmp);
1312       tmp = 0;
1313       break;
1314     }
1315 }
1316
1317 /* List and skip all channels that are listen */
1318 void list_next_channels(struct Client *cptr, int nr)
1319 {
1320   struct ListingArgs *args = cli_listing(cptr);
1321   struct Channel *chptr = args->chptr;
1322   chptr->mode.mode &= ~MODE_LISTED;
1323   while (is_listed(chptr) || --nr >= 0)
1324   {
1325     for (; chptr; chptr = chptr->next)
1326     {
1327       if (!cli_user(cptr) || (!(HasPriv(cptr, PRIV_LIST_CHAN) && IsAnOper(cptr)) && 
1328           SecretChannel(chptr) && !find_channel_member(cptr, chptr)))
1329         continue;
1330       if (chptr->users > args->min_users && chptr->users < args->max_users &&
1331           chptr->creationtime > args->min_time &&
1332           chptr->creationtime < args->max_time &&
1333           (!args->topic_limits || (*chptr->topic &&
1334           chptr->topic_time > args->min_topic_time &&
1335           chptr->topic_time < args->max_topic_time)))
1336       {
1337         if (ShowChannel(cptr,chptr))
1338           send_reply(cptr, RPL_LIST, chptr->chname, chptr->users,
1339                      chptr->topic);
1340         chptr = chptr->next;
1341         break;
1342       }
1343     }
1344     if (!chptr)
1345     {
1346       MyFree(cli_listing(cptr));
1347       cli_listing(cptr) = NULL;
1348       send_reply(cptr, RPL_LISTEND);
1349       break;
1350     }
1351   }
1352   if (chptr)
1353   {
1354     (cli_listing(cptr))->chptr = chptr;
1355     chptr->mode.mode |= MODE_LISTED;
1356   }
1357
1358   update_write(cptr);
1359 }
1360
1361 /*
1362  * Consider:
1363  *
1364  *                     client
1365  *                       |
1366  *                       c
1367  *                       |
1368  *     X --a--> A --b--> B --d--> D
1369  *                       |
1370  *                      who
1371  *
1372  * Where `who' is being KICK-ed by a "KICK" message received by server 'A'
1373  * via 'a', or on server 'B' via either 'b' or 'c', or on server D via 'd'.
1374  *
1375  * a) On server A : set CHFL_ZOMBIE for `who' (lp) and pass on the KICK.
1376  *    Remove the user immediately when no users are left on the channel.
1377  * b) On server B : remove the user (who/lp) from the channel, send a
1378  *    PART upstream (to A) and pass on the KICK.
1379  * c) KICKed by `client'; On server B : remove the user (who/lp) from the
1380  *    channel, and pass on the KICK.
1381  * d) On server D : remove the user (who/lp) from the channel, and pass on
1382  *    the KICK.
1383  *
1384  * Note:
1385  * - Setting the ZOMBIE flag never hurts, we either remove the
1386  *   client after that or we don't.
1387  * - The KICK message was already passed on, as should be in all cases.
1388  * - `who' is removed in all cases except case a) when users are left.
1389  * - A PART is only sent upstream in case b).
1390  *
1391  * 2 aug 97:
1392  *
1393  *              6
1394  *              |
1395  *  1 --- 2 --- 3 --- 4 --- 5
1396  *        |           |
1397  *      kicker       who
1398  *
1399  * We also need to turn 'who' into a zombie on servers 1 and 6,
1400  * because a KICK from 'who' (kicking someone else in that direction)
1401  * can arrive there afterwards - which should not be bounced itself.
1402  * Therefore case a) also applies for servers 1 and 6.
1403  *
1404  * --Run
1405  */
1406 void make_zombie(struct Membership* member, struct Client* who, struct Client* cptr,
1407                  struct Client* sptr, struct Channel* chptr)
1408 {
1409   assert(0 != member);
1410   assert(0 != who);
1411   assert(0 != cptr);
1412   assert(0 != chptr);
1413
1414   /* Default for case a): */
1415   SetZombie(member);
1416
1417   /* Case b) or c) ?: */
1418   if (MyUser(who))      /* server 4 */
1419   {
1420     if (IsServer(cptr)) /* Case b) ? */
1421       sendcmdto_one(who, CMD_PART, cptr, "%H", chptr);
1422     remove_user_from_channel(who, chptr);
1423     return;
1424   }
1425   if (cli_from(who) == cptr)        /* True on servers 1, 5 and 6 */
1426   {
1427     struct Client *acptr = IsServer(sptr) ? sptr : (cli_user(sptr))->server;
1428     for (; acptr != &me; acptr = (cli_serv(acptr))->up)
1429       if (acptr == (cli_user(who))->server)   /* Case d) (server 5) */
1430       {
1431         remove_user_from_channel(who, chptr);
1432         return;
1433       }
1434   }
1435
1436   /* Case a) (servers 1, 2, 3 and 6) */
1437   if (channel_all_zombies(chptr))
1438     remove_user_from_channel(who, chptr);
1439
1440   /* XXX Can't actually call Debug here; if the channel is all zombies,
1441    * chptr will no longer exist when we get here.
1442   Debug((DEBUG_INFO, "%s is now a zombie on %s", who->name, chptr->chname));
1443   */
1444 }
1445
1446 int number_of_zombies(struct Channel *chptr)
1447 {
1448   struct Membership* member;
1449   int                count = 0;
1450
1451   assert(0 != chptr);
1452   for (member = chptr->members; member; member = member->next_member) {
1453     if (IsZombie(member))
1454       ++count;
1455   }
1456   return count;
1457 }
1458
1459 /*
1460  * This helper function builds an argument string in strptr, consisting
1461  * of the original string, a space, and str1 and str2 concatenated (if,
1462  * of course, str2 is not NULL)
1463  */
1464 static void
1465 build_string(char *strptr, int *strptr_i, char *str1, char *str2, char c)
1466 {
1467   if (c)
1468     strptr[(*strptr_i)++] = c;
1469
1470   while (*str1)
1471     strptr[(*strptr_i)++] = *(str1++);
1472
1473   if (str2)
1474     while (*str2)
1475       strptr[(*strptr_i)++] = *(str2++);
1476
1477   strptr[(*strptr_i)] = '\0';
1478 }
1479
1480 /*
1481  * This is the workhorse of our ModeBuf suite; this actually generates the
1482  * output MODE commands, HACK notices, or whatever.  It's pretty complicated.
1483  */
1484 static int
1485 modebuf_flush_int(struct ModeBuf *mbuf, int all)
1486 {
1487   /* we only need the flags that don't take args right now */
1488   static int flags[] = {
1489 /*  MODE_CHANOP,        'o', */
1490 /*  MODE_VOICE,         'v', */
1491     MODE_PRIVATE,       'p',
1492     MODE_SECRET,        's',
1493     MODE_MODERATED,     'm',
1494     MODE_TOPICLIMIT,    't',
1495     MODE_INVITEONLY,    'i',
1496     MODE_NOPRIVMSGS,    'n',
1497     MODE_REGONLY,       'r',
1498 /*  MODE_KEY,           'k', */
1499 /*  MODE_BAN,           'b', */
1500 /*  MODE_LIMIT,         'l', */
1501 /*  MODE_APASS,         'A', */
1502 /*  MODE_UPASS,         'u', */
1503     0x0, 0x0
1504   };
1505   int i;
1506   int *flag_p;
1507
1508   struct Client *app_source; /* where the MODE appears to come from */
1509
1510   char addbuf[20]; /* accumulates +psmtin, etc. */
1511   int addbuf_i = 0;
1512   char rembuf[20]; /* accumulates -psmtin, etc. */
1513   int rembuf_i = 0;
1514   char *bufptr; /* we make use of indirection to simplify the code */
1515   int *bufptr_i;
1516
1517   char addstr[BUFSIZE]; /* accumulates MODE parameters to add */
1518   int addstr_i;
1519   char remstr[BUFSIZE]; /* accumulates MODE parameters to remove */
1520   int remstr_i;
1521   char *strptr; /* more indirection to simplify the code */
1522   int *strptr_i;
1523
1524   int totalbuflen = BUFSIZE - 200; /* fuzz factor -- don't overrun buffer! */
1525   int tmp;
1526
1527   char limitbuf[20]; /* convert limits to strings */
1528
1529   unsigned int limitdel = MODE_LIMIT;
1530
1531   assert(0 != mbuf);
1532
1533   /* If the ModeBuf is empty, we have nothing to do */
1534   if (mbuf->mb_add == 0 && mbuf->mb_rem == 0 && mbuf->mb_count == 0)
1535     return 0;
1536
1537   /* Ok, if we were given the OPMODE flag, or its a server, hide the source.
1538    */
1539   if (mbuf->mb_dest & MODEBUF_DEST_OPMODE || IsServer(mbuf->mb_source))
1540     app_source = &me;
1541   else
1542     app_source = mbuf->mb_source;
1543
1544   /*
1545    * Account for user we're bouncing; we have to get it in on the first
1546    * bounced MODE, or we could have problems
1547    */
1548   if (mbuf->mb_dest & MODEBUF_DEST_DEOP)
1549     totalbuflen -= 6; /* numeric nick == 5, plus one space */
1550
1551   /* Calculate the simple flags */
1552   for (flag_p = flags; flag_p[0]; flag_p += 2) {
1553     if (*flag_p & mbuf->mb_add)
1554       addbuf[addbuf_i++] = flag_p[1];
1555     else if (*flag_p & mbuf->mb_rem)
1556       rembuf[rembuf_i++] = flag_p[1];
1557   }
1558
1559   /* Now go through the modes with arguments... */
1560   for (i = 0; i < mbuf->mb_count; i++) {
1561     if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
1562       bufptr = addbuf;
1563       bufptr_i = &addbuf_i;
1564     } else {
1565       bufptr = rembuf;
1566       bufptr_i = &rembuf_i;
1567     }
1568
1569     if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE)) {
1570       tmp = strlen(cli_name(MB_CLIENT(mbuf, i)));
1571
1572       if ((totalbuflen - IRCD_MAX(5, tmp)) <= 0) /* don't overflow buffer */
1573         MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1574       else {
1575         bufptr[(*bufptr_i)++] = MB_TYPE(mbuf, i) & MODE_CHANOP ? 'o' : 'v';
1576         totalbuflen -= IRCD_MAX(5, tmp) + 1;
1577       }
1578     } else if (MB_TYPE(mbuf, i) & (MODE_BAN | MODE_APASS | MODE_UPASS)) {
1579       tmp = strlen(MB_STRING(mbuf, i));
1580
1581       if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
1582         MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1583       else {
1584         char mode_char;
1585         switch(MB_TYPE(mbuf, i) & (MODE_BAN | MODE_APASS | MODE_UPASS))
1586         {
1587           case MODE_APASS:
1588             mode_char = 'A';
1589             break;
1590           case MODE_UPASS:
1591             mode_char = 'u';
1592             break;
1593           default:
1594             mode_char = 'b';
1595             break;
1596         }
1597         bufptr[(*bufptr_i)++] = mode_char;
1598         totalbuflen -= tmp + 1;
1599       }
1600     } else if (MB_TYPE(mbuf, i) & MODE_KEY) {
1601       tmp = (mbuf->mb_dest & MODEBUF_DEST_NOKEY ? 1 :
1602              strlen(MB_STRING(mbuf, i)));
1603
1604       if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
1605         MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1606       else {
1607         bufptr[(*bufptr_i)++] = 'k';
1608         totalbuflen -= tmp + 1;
1609       }
1610     } else if (MB_TYPE(mbuf, i) & MODE_LIMIT) {
1611       /* if it's a limit, we also format the number */
1612       ircd_snprintf(0, limitbuf, sizeof(limitbuf), "%u", MB_UINT(mbuf, i));
1613
1614       tmp = strlen(limitbuf);
1615
1616       if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
1617         MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1618       else {
1619         bufptr[(*bufptr_i)++] = 'l';
1620         totalbuflen -= tmp + 1;
1621       }
1622     }
1623   }
1624
1625   /* terminate the mode strings */
1626   addbuf[addbuf_i] = '\0';
1627   rembuf[rembuf_i] = '\0';
1628
1629   /* If we're building a user visible MODE or HACK... */
1630   if (mbuf->mb_dest & (MODEBUF_DEST_CHANNEL | MODEBUF_DEST_HACK2 |
1631                        MODEBUF_DEST_HACK3   | MODEBUF_DEST_HACK4 |
1632                        MODEBUF_DEST_LOG)) {
1633     /* Set up the parameter strings */
1634     addstr[0] = '\0';
1635     addstr_i = 0;
1636     remstr[0] = '\0';
1637     remstr_i = 0;
1638
1639     for (i = 0; i < mbuf->mb_count; i++) {
1640       if (MB_TYPE(mbuf, i) & MODE_SAVE)
1641         continue;
1642
1643       if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
1644         strptr = addstr;
1645         strptr_i = &addstr_i;
1646       } else {
1647         strptr = remstr;
1648         strptr_i = &remstr_i;
1649       }
1650
1651       /* deal with clients... */
1652       if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE))
1653         build_string(strptr, strptr_i, cli_name(MB_CLIENT(mbuf, i)), 0, ' ');
1654
1655       /* deal with bans... */
1656       else if (MB_TYPE(mbuf, i) & MODE_BAN)
1657         build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
1658
1659       /* deal with keys... */
1660       else if (MB_TYPE(mbuf, i) & MODE_KEY)
1661         build_string(strptr, strptr_i, mbuf->mb_dest & MODEBUF_DEST_NOKEY ?
1662                      "*" : MB_STRING(mbuf, i), 0, ' ');
1663
1664       /* deal with invisible passwords */
1665       else if (MB_TYPE(mbuf, i) & (MODE_APASS | MODE_UPASS))
1666         build_string(strptr, strptr_i, "*", 0, ' ');
1667
1668       /*
1669        * deal with limit; note we cannot include the limit parameter if we're
1670        * removing it
1671        */
1672       else if ((MB_TYPE(mbuf, i) & (MODE_ADD | MODE_LIMIT)) ==
1673                (MODE_ADD | MODE_LIMIT))
1674         build_string(strptr, strptr_i, limitbuf, 0, ' ');
1675     }
1676
1677     /* send the messages off to their destination */
1678     if (mbuf->mb_dest & MODEBUF_DEST_HACK2)
1679       sendto_opmask_butone(0, SNO_HACK2, "HACK(2): %s MODE %s %s%s%s%s%s%s "
1680                            "[%Tu]",
1681                            cli_name(feature_bool(FEAT_HIS_SNOTICES) ?
1682                                     mbuf->mb_source : app_source),
1683                            mbuf->mb_channel->chname,
1684                            rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1685                            addbuf, remstr, addstr,
1686                            mbuf->mb_channel->creationtime);
1687
1688     if (mbuf->mb_dest & MODEBUF_DEST_HACK3)
1689       sendto_opmask_butone(0, SNO_HACK3, "BOUNCE or HACK(3): %s MODE %s "
1690                            "%s%s%s%s%s%s [%Tu]",
1691                            cli_name(feature_bool(FEAT_HIS_SNOTICES) ? 
1692                                     mbuf->mb_source : app_source),
1693                            mbuf->mb_channel->chname, rembuf_i ? "-" : "",
1694                            rembuf, addbuf_i ? "+" : "", addbuf, remstr, addstr,
1695                            mbuf->mb_channel->creationtime);
1696
1697     if (mbuf->mb_dest & MODEBUF_DEST_HACK4)
1698       sendto_opmask_butone(0, SNO_HACK4, "HACK(4): %s MODE %s %s%s%s%s%s%s "
1699                            "[%Tu]",
1700 #ifdef HEAD_IN_SAND_SNOTICES
1701                            cli_name(mbuf->mb_source),
1702 #else
1703                            cli_name(app_source),
1704 #endif
1705                            mbuf->mb_channel->chname,
1706                            rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1707                            addbuf, remstr, addstr,
1708                            mbuf->mb_channel->creationtime);
1709
1710     if (mbuf->mb_dest & MODEBUF_DEST_LOG)
1711       log_write(LS_OPERMODE, L_INFO, LOG_NOSNOTICE,
1712                 "%#C OPMODE %H %s%s%s%s%s%s", mbuf->mb_source,
1713                 mbuf->mb_channel, rembuf_i ? "-" : "", rembuf,
1714                 addbuf_i ? "+" : "", addbuf, remstr, addstr);
1715
1716     if (mbuf->mb_dest & MODEBUF_DEST_CHANNEL)
1717       sendcmdto_channel_butserv_butone(app_source, CMD_MODE, mbuf->mb_channel, NULL,
1718                                 "%H %s%s%s%s%s%s", mbuf->mb_channel,
1719                                 rembuf_i ? "-" : "", rembuf,
1720                                 addbuf_i ? "+" : "", addbuf, remstr, addstr);
1721   }
1722
1723   /* Now are we supposed to propagate to other servers? */
1724   if (mbuf->mb_dest & MODEBUF_DEST_SERVER) {
1725     /* set up parameter string */
1726     addstr[0] = '\0';
1727     addstr_i = 0;
1728     remstr[0] = '\0';
1729     remstr_i = 0;
1730
1731     /*
1732      * limit is supressed if we're removing it; we have to figure out which
1733      * direction is the direction for it to be removed, though...
1734      */
1735     limitdel |= (mbuf->mb_dest & MODEBUF_DEST_HACK2) ? MODE_DEL : MODE_ADD;
1736
1737     for (i = 0; i < mbuf->mb_count; i++) {
1738       if (MB_TYPE(mbuf, i) & MODE_SAVE)
1739         continue;
1740
1741       if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
1742         strptr = addstr;
1743         strptr_i = &addstr_i;
1744       } else {
1745         strptr = remstr;
1746         strptr_i = &remstr_i;
1747       }
1748
1749       /* deal with modes that take clients */
1750       if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE))
1751         build_string(strptr, strptr_i, NumNick(MB_CLIENT(mbuf, i)), ' ');
1752
1753       /* deal with modes that take strings */
1754       else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN | MODE_APASS | MODE_UPASS))
1755         build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
1756
1757       /*
1758        * deal with the limit.  Logic here is complicated; if HACK2 is set,
1759        * we're bouncing the mode, so sense is reversed, and we have to
1760        * include the original limit if it looks like it's being removed
1761        */
1762       else if ((MB_TYPE(mbuf, i) & limitdel) == limitdel)
1763         build_string(strptr, strptr_i, limitbuf, 0, ' ');
1764     }
1765
1766     /* we were told to deop the source */
1767     if (mbuf->mb_dest & MODEBUF_DEST_DEOP) {
1768       addbuf[addbuf_i++] = 'o'; /* remember, sense is reversed */
1769       addbuf[addbuf_i] = '\0'; /* terminate the string... */
1770       build_string(addstr, &addstr_i, NumNick(mbuf->mb_source), ' ');
1771
1772       /* mark that we've done this, so we don't do it again */
1773       mbuf->mb_dest &= ~MODEBUF_DEST_DEOP;
1774     }
1775
1776     if (mbuf->mb_dest & MODEBUF_DEST_OPMODE) {
1777       /* If OPMODE was set, we're propagating the mode as an OPMODE message */
1778       sendcmdto_serv_butone(mbuf->mb_source, CMD_OPMODE, mbuf->mb_connect,
1779                             "%H %s%s%s%s%s%s", mbuf->mb_channel,
1780                             rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1781                             addbuf, remstr, addstr);
1782     } else if (mbuf->mb_dest & MODEBUF_DEST_BOUNCE) {
1783       /*
1784        * If HACK2 was set, we're bouncing; we send the MODE back to the
1785        * connection we got it from with the senses reversed and a TS of 0;
1786        * origin is us
1787        */
1788       sendcmdto_one(&me, CMD_MODE, mbuf->mb_connect, "%H %s%s%s%s%s%s %Tu",
1789                     mbuf->mb_channel, addbuf_i ? "-" : "", addbuf,
1790                     rembuf_i ? "+" : "", rembuf, addstr, remstr,
1791                     mbuf->mb_channel->creationtime);
1792     } else {
1793       /*
1794        * We're propagating a normal MODE command to the rest of the network;
1795        * we send the actual channel TS unless this is a HACK3 or a HACK4
1796        */
1797       if (IsServer(mbuf->mb_source))
1798         sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
1799                               "%H %s%s%s%s%s%s %Tu", mbuf->mb_channel,
1800                               rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1801                               addbuf, remstr, addstr,
1802                               (mbuf->mb_dest & MODEBUF_DEST_HACK4) ? 0 :
1803                               mbuf->mb_channel->creationtime);
1804       else
1805         sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
1806                               "%H %s%s%s%s%s%s", mbuf->mb_channel,
1807                               rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1808                               addbuf, remstr, addstr);
1809     }
1810   }
1811
1812   /* We've drained the ModeBuf... */
1813   mbuf->mb_add = 0;
1814   mbuf->mb_rem = 0;
1815   mbuf->mb_count = 0;
1816
1817   /* reinitialize the mode-with-arg slots */
1818   for (i = 0; i < MAXMODEPARAMS; i++) {
1819     /* If we saved any, pack them down */
1820     if (MB_TYPE(mbuf, i) & MODE_SAVE) {
1821       mbuf->mb_modeargs[mbuf->mb_count] = mbuf->mb_modeargs[i];
1822       MB_TYPE(mbuf, mbuf->mb_count) &= ~MODE_SAVE; /* don't save anymore */
1823
1824       if (mbuf->mb_count++ == i) /* don't overwrite our hard work */
1825         continue;
1826     } else if (MB_TYPE(mbuf, i) & MODE_FREE)
1827       MyFree(MB_STRING(mbuf, i)); /* free string if needed */
1828
1829     MB_TYPE(mbuf, i) = 0;
1830     MB_UINT(mbuf, i) = 0;
1831   }
1832
1833   /* If we're supposed to flush it all, do so--all hail tail recursion */
1834   if (all && mbuf->mb_count)
1835     return modebuf_flush_int(mbuf, 1);
1836
1837   return 0;
1838 }
1839
1840 /*
1841  * This routine just initializes a ModeBuf structure with the information
1842  * needed and the options given.
1843  */
1844 void
1845 modebuf_init(struct ModeBuf *mbuf, struct Client *source,
1846              struct Client *connect, struct Channel *chan, unsigned int dest)
1847 {
1848   int i;
1849
1850   assert(0 != mbuf);
1851   assert(0 != source);
1852   assert(0 != chan);
1853   assert(0 != dest);
1854
1855   mbuf->mb_add = 0;
1856   mbuf->mb_rem = 0;
1857   mbuf->mb_source = source;
1858   mbuf->mb_connect = connect;
1859   mbuf->mb_channel = chan;
1860   mbuf->mb_dest = dest;
1861   mbuf->mb_count = 0;
1862
1863   /* clear each mode-with-parameter slot */
1864   for (i = 0; i < MAXMODEPARAMS; i++) {
1865     MB_TYPE(mbuf, i) = 0;
1866     MB_UINT(mbuf, i) = 0;
1867   }
1868 }
1869
1870 /*
1871  * This routine simply adds modes to be added or deleted; do a binary OR
1872  * with either MODE_ADD or MODE_DEL
1873  */
1874 void
1875 modebuf_mode(struct ModeBuf *mbuf, unsigned int mode)
1876 {
1877   assert(0 != mbuf);
1878   assert(0 != (mode & (MODE_ADD | MODE_DEL)));
1879
1880   mode &= (MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET | MODE_MODERATED |
1881            MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS | MODE_REGONLY);
1882
1883   if (!(mode & ~(MODE_ADD | MODE_DEL))) /* don't add empty modes... */
1884     return;
1885
1886   if (mode & MODE_ADD) {
1887     mbuf->mb_rem &= ~mode;
1888     mbuf->mb_add |= mode;
1889   } else {
1890     mbuf->mb_add &= ~mode;
1891     mbuf->mb_rem |= mode;
1892   }
1893 }
1894
1895 /*
1896  * This routine adds a mode to be added or deleted that takes a unsigned
1897  * int parameter; mode may *only* be the relevant mode flag ORed with one
1898  * of MODE_ADD or MODE_DEL
1899  */
1900 void
1901 modebuf_mode_uint(struct ModeBuf *mbuf, unsigned int mode, unsigned int uint)
1902 {
1903   assert(0 != mbuf);
1904   assert(0 != (mode & (MODE_ADD | MODE_DEL)));
1905
1906   MB_TYPE(mbuf, mbuf->mb_count) = mode;
1907   MB_UINT(mbuf, mbuf->mb_count) = uint;
1908
1909   /* when we've reached the maximal count, flush the buffer */
1910   if (++mbuf->mb_count >=
1911       (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
1912     modebuf_flush_int(mbuf, 0);
1913 }
1914
1915 /*
1916  * This routine adds a mode to be added or deleted that takes a string
1917  * parameter; mode may *only* be the relevant mode flag ORed with one of
1918  * MODE_ADD or MODE_DEL
1919  */
1920 void
1921 modebuf_mode_string(struct ModeBuf *mbuf, unsigned int mode, char *string,
1922                     int free)
1923 {
1924   assert(0 != mbuf);
1925   assert(0 != (mode & (MODE_ADD | MODE_DEL)));
1926
1927   MB_TYPE(mbuf, mbuf->mb_count) = mode | (free ? MODE_FREE : 0);
1928   MB_STRING(mbuf, mbuf->mb_count) = string;
1929
1930   /* when we've reached the maximal count, flush the buffer */
1931   if (++mbuf->mb_count >=
1932       (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
1933     modebuf_flush_int(mbuf, 0);
1934 }
1935
1936 /*
1937  * This routine adds a mode to be added or deleted that takes a client
1938  * parameter; mode may *only* be the relevant mode flag ORed with one of
1939  * MODE_ADD or MODE_DEL
1940  */
1941 void
1942 modebuf_mode_client(struct ModeBuf *mbuf, unsigned int mode,
1943                     struct Client *client)
1944 {
1945   assert(0 != mbuf);
1946   assert(0 != (mode & (MODE_ADD | MODE_DEL)));
1947
1948   MB_TYPE(mbuf, mbuf->mb_count) = mode;
1949   MB_CLIENT(mbuf, mbuf->mb_count) = client;
1950
1951   /* when we've reached the maximal count, flush the buffer */
1952   if (++mbuf->mb_count >=
1953       (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
1954     modebuf_flush_int(mbuf, 0);
1955 }
1956
1957 /*
1958  * This is the exported binding for modebuf_flush()
1959  */
1960 int
1961 modebuf_flush(struct ModeBuf *mbuf)
1962 {
1963   return modebuf_flush_int(mbuf, 1);
1964 }
1965
1966 /*
1967  * This extracts the simple modes contained in mbuf
1968  */
1969 void
1970 modebuf_extract(struct ModeBuf *mbuf, char *buf)
1971 {
1972   static int flags[] = {
1973 /*  MODE_CHANOP,        'o', */
1974 /*  MODE_VOICE,         'v', */
1975     MODE_PRIVATE,       'p',
1976     MODE_SECRET,        's',
1977     MODE_MODERATED,     'm',
1978     MODE_TOPICLIMIT,    't',
1979     MODE_INVITEONLY,    'i',
1980     MODE_NOPRIVMSGS,    'n',
1981     MODE_KEY,           'k',
1982     MODE_APASS,         'A',
1983     MODE_UPASS,         'u',
1984 /*  MODE_BAN,           'b', */
1985     MODE_LIMIT,         'l',
1986     MODE_REGONLY,       'r',
1987     0x0, 0x0
1988   };
1989   unsigned int add;
1990   int i, bufpos = 0, len;
1991   int *flag_p;
1992   char *key = 0, limitbuf[20];
1993   char *apass = 0, *upass = 0;
1994
1995   assert(0 != mbuf);
1996   assert(0 != buf);
1997
1998   buf[0] = '\0';
1999
2000   add = mbuf->mb_add;
2001
2002   for (i = 0; i < mbuf->mb_count; i++) { /* find keys and limits */
2003     if (MB_TYPE(mbuf, i) & MODE_ADD) {
2004       add |= MB_TYPE(mbuf, i) & (MODE_KEY | MODE_LIMIT | MODE_APASS | MODE_UPASS);
2005
2006       if (MB_TYPE(mbuf, i) & MODE_KEY) /* keep strings */
2007         key = MB_STRING(mbuf, i);
2008       else if (MB_TYPE(mbuf, i) & MODE_LIMIT)
2009         ircd_snprintf(0, limitbuf, sizeof(limitbuf), "%u", MB_UINT(mbuf, i));
2010       else if (MB_TYPE(mbuf, i) & MODE_UPASS)
2011         upass = MB_STRING(mbuf, i);
2012       else if (MB_TYPE(mbuf, i) & MODE_APASS)
2013         apass = MB_STRING(mbuf, i);
2014     }
2015   }
2016
2017   if (!add)
2018     return;
2019
2020   buf[bufpos++] = '+'; /* start building buffer */
2021
2022   for (flag_p = flags; flag_p[0]; flag_p += 2)
2023     if (*flag_p & add)
2024       buf[bufpos++] = flag_p[1];
2025
2026   for (i = 0, len = bufpos; i < len; i++) {
2027     if (buf[i] == 'k')
2028       build_string(buf, &bufpos, key, 0, ' ');
2029     else if (buf[i] == 'l')
2030       build_string(buf, &bufpos, limitbuf, 0, ' ');
2031     else if (buf[i] == 'u')
2032       build_string(buf, &bufpos, upass, 0, ' ');
2033     else if (buf[i] == 'A')
2034       build_string(buf, &bufpos, apass, 0, ' ');
2035   }
2036
2037   buf[bufpos] = '\0';
2038
2039   return;
2040 }
2041
2042 /*
2043  * Simple function to invalidate bans
2044  */
2045 void
2046 mode_ban_invalidate(struct Channel *chan)
2047 {
2048   struct Membership *member;
2049
2050   for (member = chan->members; member; member = member->next_member)
2051     ClearBanValid(member);
2052 }
2053
2054 /*
2055  * Simple function to drop invite structures
2056  */
2057 void
2058 mode_invite_clear(struct Channel *chan)
2059 {
2060   while (chan->invites)
2061     del_invite(chan->invites->value.cptr, chan);
2062 }
2063
2064 /* What we've done for mode_parse so far... */
2065 #define DONE_LIMIT      0x01    /* We've set the limit */
2066 #define DONE_KEY        0x02    /* We've set the key */
2067 #define DONE_BANLIST    0x04    /* We've sent the ban list */
2068 #define DONE_NOTOPER    0x08    /* We've sent a "Not oper" error */
2069 #define DONE_BANCLEAN   0x10    /* We've cleaned bans... */
2070 #define DONE_UPASS      0x20    /* We've set user pass */
2071 #define DONE_APASS      0x40    /* We've set admin pass */
2072
2073 struct ParseState {
2074   struct ModeBuf *mbuf;
2075   struct Client *cptr;
2076   struct Client *sptr;
2077   struct Channel *chptr;
2078   struct Membership *member;
2079   int parc;
2080   char **parv;
2081   unsigned int flags;
2082   unsigned int dir;
2083   unsigned int done;
2084   unsigned int add;
2085   unsigned int del;
2086   int args_used;
2087   int max_args;
2088   int numbans;
2089   struct SLink banlist[MAXPARA];
2090   struct {
2091     unsigned int flag;
2092     struct Client *client;
2093   } cli_change[MAXPARA];
2094 };
2095
2096 /*
2097  * Here's a helper function to deal with sending along "Not oper" or
2098  * "Not member" messages
2099  */
2100 static void
2101 send_notoper(struct ParseState *state)
2102 {
2103   if (state->done & DONE_NOTOPER)
2104     return;
2105
2106   send_reply(state->sptr, (state->flags & MODE_PARSE_NOTOPER) ?
2107              ERR_CHANOPRIVSNEEDED : ERR_NOTONCHANNEL, state->chptr->chname);
2108
2109   state->done |= DONE_NOTOPER;
2110 }
2111
2112 /*
2113  * Helper function to convert limits
2114  */
2115 static void
2116 mode_parse_limit(struct ParseState *state, int *flag_p)
2117 {
2118   unsigned int t_limit;
2119
2120   if (state->dir == MODE_ADD) { /* convert arg only if adding limit */
2121     if (MyUser(state->sptr) && state->max_args <= 0) /* too many args? */
2122       return;
2123
2124     if (state->parc <= 0) { /* warn if not enough args */
2125       if (MyUser(state->sptr))
2126         need_more_params(state->sptr, "MODE +l");
2127       return;
2128     }
2129
2130     t_limit = strtoul(state->parv[state->args_used++], 0, 10); /* grab arg */
2131     state->parc--;
2132     state->max_args--;
2133
2134     if (!(state->flags & MODE_PARSE_WIPEOUT) &&
2135         (!t_limit || t_limit == state->chptr->mode.limit))
2136       return;
2137   } else
2138     t_limit = state->chptr->mode.limit;
2139
2140   /* If they're not an oper, they can't change modes */
2141   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2142     send_notoper(state);
2143     return;
2144   }
2145
2146   if (state->done & DONE_LIMIT) /* allow limit to be set only once */
2147     return;
2148   state->done |= DONE_LIMIT;
2149
2150   if (!state->mbuf)
2151     return;
2152
2153   modebuf_mode_uint(state->mbuf, state->dir | flag_p[0], t_limit);
2154
2155   if (state->flags & MODE_PARSE_SET) { /* set the limit */
2156     if (state->dir & MODE_ADD) {
2157       state->chptr->mode.mode |= flag_p[0];
2158       state->chptr->mode.limit = t_limit;
2159     } else {
2160       state->chptr->mode.mode &= ~flag_p[0];
2161       state->chptr->mode.limit = 0;
2162     }
2163   }
2164 }
2165
2166 /*
2167  * Helper function to convert keys
2168  */
2169 static void
2170 mode_parse_key(struct ParseState *state, int *flag_p)
2171 {
2172   char *t_str, *s;
2173   int t_len;
2174
2175   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2176     return;
2177
2178   if (state->parc <= 0) { /* warn if not enough args */
2179     if (MyUser(state->sptr))
2180       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +k" :
2181                        "MODE -k");
2182     return;
2183   }
2184
2185   t_str = state->parv[state->args_used++]; /* grab arg */
2186   state->parc--;
2187   state->max_args--;
2188
2189   /* If they're not an oper, they can't change modes */
2190   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2191     send_notoper(state);
2192     return;
2193   }
2194
2195   if (state->done & DONE_KEY) /* allow key to be set only once */
2196     return;
2197   state->done |= DONE_KEY;
2198
2199   t_len = KEYLEN;
2200
2201   /* clean up the key string */
2202   s = t_str;
2203   while (*++s > ' ' && *s != ':' && --t_len)
2204     ;
2205   *s = '\0';
2206
2207   if (!*t_str) { /* warn if empty */
2208     if (MyUser(state->sptr))
2209       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +k" :
2210                        "MODE -k");
2211     return;
2212   }
2213
2214   if (!state->mbuf)
2215     return;
2216
2217   /* can't add a key if one is set, nor can one remove the wrong key */
2218   if (!(state->flags & MODE_PARSE_FORCE))
2219     if ((state->dir == MODE_ADD && *state->chptr->mode.key) ||
2220         (state->dir == MODE_DEL &&
2221          ircd_strcmp(state->chptr->mode.key, t_str))) {
2222       send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
2223       return;
2224     }
2225
2226   if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
2227       !ircd_strcmp(state->chptr->mode.key, t_str))
2228     return; /* no key change */
2229
2230   if (state->flags & MODE_PARSE_BOUNCE) {
2231     if (*state->chptr->mode.key) /* reset old key */
2232       modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
2233                           state->chptr->mode.key, 0);
2234     else /* remove new bogus key */
2235       modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
2236   } else /* send new key */
2237     modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
2238
2239   if (state->flags & MODE_PARSE_SET) {
2240     if (state->dir == MODE_ADD) /* set the new key */
2241       ircd_strncpy(state->chptr->mode.key, t_str, KEYLEN);
2242     else /* remove the old key */
2243       *state->chptr->mode.key = '\0';
2244   }
2245 }
2246
2247 /*
2248  * Helper function to convert user passes
2249  */
2250 static void
2251 mode_parse_upass(struct ParseState *state, int *flag_p)
2252 {
2253   char *t_str, *s;
2254   int t_len;
2255
2256   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2257     return;
2258
2259   if (state->parc <= 0) { /* warn if not enough args */
2260     if (MyUser(state->sptr))
2261       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +u" :
2262                        "MODE -u");
2263     return;
2264   }
2265
2266   t_str = state->parv[state->args_used++]; /* grab arg */
2267   state->parc--;
2268   state->max_args--;
2269
2270   /* If they're not an oper, they can't change modes */
2271   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2272     send_notoper(state);
2273     return;
2274   }
2275
2276   /* If they are not the channel manager, they are not allowed to change it */
2277   if (MyUser(state->sptr) && !IsChannelManager(state->member)) {
2278     if (*state->chptr->mode.apass) {
2279       send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
2280           "Use /JOIN", state->chptr->chname, "<AdminPass>.");
2281     } else {
2282       send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
2283           "Re-create the channel.  The channel must be *empty* for",
2284           TStime() - state->chptr->creationtime >= 171000 ? "48 contiguous hours" : "a minute or two",
2285           "before it can be recreated.");
2286     }
2287     return;
2288   }
2289  
2290   if (state->done & DONE_UPASS) /* allow upass to be set only once */
2291     return;
2292   state->done |= DONE_UPASS;
2293
2294   t_len = PASSLEN + 1;
2295
2296   /* clean up the upass string */
2297   s = t_str;
2298   while (*++s > ' ' && *s != ':' && --t_len)
2299     ;
2300   *s = '\0';
2301
2302   if (!*t_str) { /* warn if empty */
2303     if (MyUser(state->sptr))
2304       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +u" :
2305                        "MODE -u");
2306     return;
2307   }
2308
2309   if (!state->mbuf)
2310     return;
2311
2312   if (!(state->flags & MODE_PARSE_FORCE))
2313     /* can't add the upass while apass is not set */
2314     if (state->dir == MODE_ADD && !*state->chptr->mode.apass) {
2315       send_reply(state->sptr, ERR_UPASSNOTSET, state->chptr->chname, state->chptr->chname);
2316       return;
2317     }
2318     /* can't add a upass if one is set, nor can one remove the wrong upass */
2319     if ((state->dir == MODE_ADD && *state->chptr->mode.upass) ||
2320         (state->dir == MODE_DEL &&
2321          ircd_strcmp(state->chptr->mode.upass, t_str))) {
2322       send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
2323       return;
2324     }
2325
2326   if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
2327       !ircd_strcmp(state->chptr->mode.upass, t_str))
2328     return; /* no upass change */
2329
2330   if (state->flags & MODE_PARSE_BOUNCE) {
2331     if (*state->chptr->mode.upass) /* reset old upass */
2332       modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
2333                           state->chptr->mode.upass, 0);
2334     else /* remove new bogus upass */
2335       modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
2336   } else /* send new upass */
2337     modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
2338
2339   if (state->flags & MODE_PARSE_SET) {
2340     if (state->dir == MODE_ADD) /* set the new upass */
2341       ircd_strncpy(state->chptr->mode.upass, t_str, PASSLEN);
2342     else /* remove the old upass */
2343       *state->chptr->mode.upass = '\0';
2344   }
2345 }
2346
2347 /*
2348  * Helper function to convert admin passes
2349  */
2350 static void
2351 mode_parse_apass(struct ParseState *state, int *flag_p)
2352 {
2353   char *t_str, *s;
2354   int t_len;
2355
2356   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2357     return;
2358
2359   if (state->parc <= 0) { /* warn if not enough args */
2360     if (MyUser(state->sptr))
2361       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +A" :
2362                        "MODE -A");
2363     return;
2364   }
2365
2366   t_str = state->parv[state->args_used++]; /* grab arg */
2367   state->parc--;
2368   state->max_args--;
2369
2370   /* If they're not an oper, they can't change modes */
2371   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2372     send_notoper(state);
2373     return;
2374   }
2375
2376   /* Don't allow to change the Apass if the channel is older than 48 hours. */
2377   if (TStime() - state->chptr->creationtime >= 172800 && !IsAnOper(state->sptr)) {
2378     send_reply(state->sptr, ERR_CHANSECURED, state->chptr->chname);
2379     return;
2380   }
2381
2382   /* If they are not the channel manager, they are not allowed to change it */
2383   if (MyUser(state->sptr) && !IsChannelManager(state->member)) {
2384     if (*state->chptr->mode.apass) {
2385       send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
2386           "Use /JOIN", state->chptr->chname, "<AdminPass>.");
2387     } else {
2388       send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
2389           "Re-create the channel.  The channel must be *empty* for",
2390           "at least a whole minute", "before it can be recreated.");
2391     }
2392     return;
2393   }
2394  
2395   if (state->done & DONE_APASS) /* allow apass to be set only once */
2396     return;
2397   state->done |= DONE_APASS;
2398
2399   t_len = PASSLEN + 1;
2400
2401   /* clean up the apass string */
2402   s = t_str;
2403   while (*++s > ' ' && *s != ':' && --t_len)
2404     ;
2405   *s = '\0';
2406
2407   if (!*t_str) { /* warn if empty */
2408     if (MyUser(state->sptr))
2409       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +A" :
2410                        "MODE -A");
2411     return;
2412   }
2413
2414   if (!state->mbuf)
2415     return;
2416
2417   if (!(state->flags & MODE_PARSE_FORCE)) {
2418     /* can't remove the apass while upass is still set */
2419     if (state->dir == MODE_DEL && *state->chptr->mode.upass) {
2420       send_reply(state->sptr, ERR_UPASSSET, state->chptr->chname, state->chptr->chname);
2421       return;
2422     }
2423     /* can't add an apass if one is set, nor can one remove the wrong apass */
2424     if ((state->dir == MODE_ADD && *state->chptr->mode.apass) ||
2425         (state->dir == MODE_DEL && ircd_strcmp(state->chptr->mode.apass, t_str))) {
2426       send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
2427       return;
2428     }
2429   }
2430
2431   if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
2432       !ircd_strcmp(state->chptr->mode.apass, t_str))
2433     return; /* no apass change */
2434
2435   if (state->flags & MODE_PARSE_BOUNCE) {
2436     if (*state->chptr->mode.apass) /* reset old apass */
2437       modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
2438                           state->chptr->mode.apass, 0);
2439     else /* remove new bogus apass */
2440       modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
2441   } else /* send new apass */
2442     modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
2443
2444   if (state->flags & MODE_PARSE_SET) {
2445     if (state->dir == MODE_ADD) { /* set the new apass */
2446       /* Make it VERY clear to the user that this is a one-time password */
2447       ircd_strncpy(state->chptr->mode.apass, t_str, PASSLEN);
2448       if (MyUser(state->sptr)) {
2449         send_reply(state->sptr, RPL_APASSWARN,
2450             "Channel Admin password (+A) set to '", state->chptr->mode.apass, "'. ",
2451             "Are you SURE you want to use this as Admin password? ",
2452             "You will NOT be able to change this password anymore once the channel is more than 48 hours old!");
2453         send_reply(state->sptr, RPL_APASSWARN,
2454             "Use \"/MODE ", state->chptr->chname, " -A ", state->chptr->mode.apass,
2455             "\" to remove the password and then immediately set a new one. "
2456             "IMPORTANT: YOU CANNOT RECOVER THIS PASSWORD, EVER; "
2457             "WRITE THE PASSWORD DOWN (don't store this rescue password on disk)! "
2458             "Now set the channel user password (+u).");
2459       }
2460     } else { /* remove the old apass */
2461       *state->chptr->mode.apass = '\0';
2462       if (MyUser(state->sptr))
2463         send_reply(state->sptr, RPL_APASSWARN,
2464             "WARNING: You removed the channel Admin password MODE (+A). ",
2465             "If you would disconnect or leave the channel without setting a new password then you will ",
2466             "not be able to set it again and lose ownership of this channel! ",
2467             "SET A NEW PASSWORD NOW!", "");
2468     }
2469   }
2470 }
2471
2472 /*
2473  * Helper function to convert bans
2474  */
2475 static void
2476 mode_parse_ban(struct ParseState *state, int *flag_p)
2477 {
2478   char *t_str, *s;
2479   struct SLink *ban, *newban = 0;
2480
2481   if (state->parc <= 0) { /* Not enough args, send ban list */
2482     if (MyUser(state->sptr) && !(state->done & DONE_BANLIST)) {
2483       send_ban_list(state->sptr, state->chptr);
2484       state->done |= DONE_BANLIST;
2485     }
2486
2487     return;
2488   }
2489
2490   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2491     return;
2492
2493   t_str = state->parv[state->args_used++]; /* grab arg */
2494   state->parc--;
2495   state->max_args--;
2496
2497   /* If they're not an oper, they can't change modes */
2498   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2499     send_notoper(state);
2500     return;
2501   }
2502
2503   if ((s = strchr(t_str, ' ')))
2504     *s = '\0';
2505
2506   if (!*t_str || *t_str == ':') { /* warn if empty */
2507     if (MyUser(state->sptr))
2508       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +b" :
2509                        "MODE -b");
2510     return;
2511   }
2512
2513   t_str = collapse(pretty_mask(t_str));
2514
2515   /* remember the ban for the moment... */
2516   if (state->dir == MODE_ADD) {
2517     newban = state->banlist + (state->numbans++);
2518     newban->next = 0;
2519
2520     DupString(newban->value.ban.banstr, t_str);
2521     newban->value.ban.who = cli_name(state->sptr);
2522     newban->value.ban.when = TStime();
2523
2524     newban->flags = CHFL_BAN | MODE_ADD;
2525
2526     if ((s = strrchr(t_str, '@')) && check_if_ipmask(s + 1))
2527       newban->flags |= CHFL_BAN_IPMASK;
2528   }
2529
2530   if (!state->chptr->banlist) {
2531     state->chptr->banlist = newban; /* add our ban with its flags */
2532     state->done |= DONE_BANCLEAN;
2533     return;
2534   }
2535
2536   /* Go through all bans */
2537   for (ban = state->chptr->banlist; ban; ban = ban->next) {
2538     /* first, clean the ban flags up a bit */
2539     if (!(state->done & DONE_BANCLEAN))
2540       /* Note: We're overloading *lots* of bits here; be careful! */
2541       ban->flags &= ~(MODE_ADD | MODE_DEL | CHFL_BAN_OVERLAPPED);
2542
2543     /* Bit meanings:
2544      *
2545      * MODE_ADD            - Ban was added; if we're bouncing modes,
2546      *                       then we'll remove it below; otherwise,
2547      *                       we'll have to allocate a real ban
2548      *
2549      * MODE_DEL            - Ban was marked for deletion; if we're
2550      *                       bouncing modes, we'll have to re-add it,
2551      *                       otherwise, we'll have to remove it
2552      *
2553      * CHFL_BAN_OVERLAPPED - The ban we added turns out to overlap
2554      *                       with a ban already set; if we're
2555      *                       bouncing modes, we'll have to bounce
2556      *                       this one; otherwise, we'll just ignore
2557      *                       it when we process added bans
2558      */
2559
2560     if (state->dir == MODE_DEL && !ircd_strcmp(ban->value.ban.banstr, t_str)) {
2561       ban->flags |= MODE_DEL; /* delete one ban */
2562
2563       if (state->done & DONE_BANCLEAN) /* If we're cleaning, finish */
2564         break;
2565     } else if (state->dir == MODE_ADD) {
2566       /* if the ban already exists, don't worry about it */
2567       if (!ircd_strcmp(ban->value.ban.banstr, t_str)) {
2568         newban->flags &= ~MODE_ADD; /* don't add ban at all */
2569         MyFree(newban->value.ban.banstr); /* stopper a leak */
2570         state->numbans--; /* deallocate last ban */
2571         if (state->done & DONE_BANCLEAN) /* If we're cleaning, finish */
2572           break;
2573       } else if (!mmatch(ban->value.ban.banstr, t_str)) {
2574         if (!(ban->flags & MODE_DEL))
2575           newban->flags |= CHFL_BAN_OVERLAPPED; /* our ban overlaps */
2576       } else if (!mmatch(t_str, ban->value.ban.banstr))
2577         ban->flags |= MODE_DEL; /* mark ban for deletion: overlapping */
2578
2579       if (!ban->next && (newban->flags & MODE_ADD))
2580       {
2581         ban->next = newban; /* add our ban with its flags */
2582         break; /* get out of loop */
2583       }
2584     }
2585   }
2586   state->done |= DONE_BANCLEAN;
2587 }
2588
2589 /*
2590  * This is the bottom half of the ban processor
2591  */
2592 static void
2593 mode_process_bans(struct ParseState *state)
2594 {
2595   struct SLink *ban, *newban, *prevban, *nextban;
2596   int count = 0;
2597   int len = 0;
2598   int banlen;
2599   int changed = 0;
2600
2601   for (prevban = 0, ban = state->chptr->banlist; ban; ban = nextban) {
2602     count++;
2603     banlen = strlen(ban->value.ban.banstr);
2604     len += banlen;
2605     nextban = ban->next;
2606
2607     if ((ban->flags & (MODE_DEL | MODE_ADD)) == (MODE_DEL | MODE_ADD)) {
2608       if (prevban)
2609         prevban->next = 0; /* Break the list; ban isn't a real ban */
2610       else
2611         state->chptr->banlist = 0;
2612
2613       count--;
2614       len -= banlen;
2615
2616       MyFree(ban->value.ban.banstr);
2617
2618       continue;
2619     } else if (ban->flags & MODE_DEL) { /* Deleted a ban? */
2620       modebuf_mode_string(state->mbuf, MODE_DEL | MODE_BAN,
2621                           ban->value.ban.banstr,
2622                           state->flags & MODE_PARSE_SET);
2623
2624       if (state->flags & MODE_PARSE_SET) { /* Ok, make it take effect */
2625         if (prevban) /* clip it out of the list... */
2626           prevban->next = ban->next;
2627         else
2628           state->chptr->banlist = ban->next;
2629
2630         count--;
2631         len -= banlen;
2632
2633         MyFree(ban->value.ban.who);
2634         free_link(ban);
2635
2636         changed++;
2637         continue; /* next ban; keep prevban like it is */
2638       } else
2639         ban->flags &= (CHFL_BAN | CHFL_BAN_IPMASK); /* unset other flags */
2640     } else if (ban->flags & MODE_ADD) { /* adding a ban? */
2641       if (prevban)
2642         prevban->next = 0; /* Break the list; ban isn't a real ban */
2643       else
2644         state->chptr->banlist = 0;
2645
2646       /* If we're supposed to ignore it, do so. */
2647       if (ban->flags & CHFL_BAN_OVERLAPPED &&
2648           !(state->flags & MODE_PARSE_BOUNCE)) {
2649         count--;
2650         len -= banlen;
2651
2652         MyFree(ban->value.ban.banstr);
2653       } else {
2654         if (state->flags & MODE_PARSE_SET && MyUser(state->sptr) &&
2655             (len > (feature_int(FEAT_AVBANLEN) * feature_int(FEAT_MAXBANS)) ||
2656              count > feature_int(FEAT_MAXBANS))) {
2657           send_reply(state->sptr, ERR_BANLISTFULL, state->chptr->chname,
2658                      ban->value.ban.banstr);
2659           count--;
2660           len -= banlen;
2661
2662           MyFree(ban->value.ban.banstr);
2663         } else {
2664           /* add the ban to the buffer */
2665           modebuf_mode_string(state->mbuf, MODE_ADD | MODE_BAN,
2666                               ban->value.ban.banstr,
2667                               !(state->flags & MODE_PARSE_SET));
2668
2669           if (state->flags & MODE_PARSE_SET) { /* create a new ban */
2670             newban = make_link();
2671             newban->value.ban.banstr = ban->value.ban.banstr;
2672             DupString(newban->value.ban.who, ban->value.ban.who);
2673             newban->value.ban.when = ban->value.ban.when;
2674             newban->flags = ban->flags & (CHFL_BAN | CHFL_BAN_IPMASK);
2675
2676             newban->next = state->chptr->banlist; /* and link it in */
2677             state->chptr->banlist = newban;
2678
2679             changed++;
2680           }
2681         }
2682       }
2683     }
2684
2685     prevban = ban;
2686   } /* for (prevban = 0, ban = state->chptr->banlist; ban; ban = nextban) { */
2687
2688   if (changed) /* if we changed the ban list, we must invalidate the bans */
2689     mode_ban_invalidate(state->chptr);
2690 }
2691
2692 /*
2693  * Helper function to process client changes
2694  */
2695 static void
2696 mode_parse_client(struct ParseState *state, int *flag_p)
2697 {
2698   char *t_str;
2699   struct Client *acptr;
2700   int i;
2701
2702   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2703     return;
2704
2705   if (state->parc <= 0) /* return if not enough args */
2706     return;
2707
2708   t_str = state->parv[state->args_used++]; /* grab arg */
2709   state->parc--;
2710   state->max_args--;
2711
2712   /* If they're not an oper, they can't change modes */
2713   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2714     send_notoper(state);
2715     return;
2716   }
2717
2718   if (MyUser(state->sptr)) /* find client we're manipulating */
2719     acptr = find_chasing(state->sptr, t_str, NULL);
2720   else
2721     acptr = findNUser(t_str);
2722
2723   if (!acptr)
2724     return; /* find_chasing() already reported an error to the user */
2725
2726   for (i = 0; i < MAXPARA; i++) /* find an element to stick them in */
2727     if (!state->cli_change[i].flag || (state->cli_change[i].client == acptr &&
2728                                        state->cli_change[i].flag & flag_p[0]))
2729       break; /* found a slot */
2730
2731   /* Store what we're doing to them */
2732   state->cli_change[i].flag = state->dir | flag_p[0];
2733   state->cli_change[i].client = acptr;
2734 }
2735
2736 /*
2737  * Helper function to process the changed client list
2738  */
2739 static void
2740 mode_process_clients(struct ParseState *state)
2741 {
2742   int i;
2743   struct Membership *member;
2744
2745   for (i = 0; state->cli_change[i].flag; i++) {
2746     assert(0 != state->cli_change[i].client);
2747
2748     /* look up member link */
2749     if (!(member = find_member_link(state->chptr,
2750                                     state->cli_change[i].client)) ||
2751         (MyUser(state->sptr) && IsZombie(member))) {
2752       if (MyUser(state->sptr))
2753         send_reply(state->sptr, ERR_USERNOTINCHANNEL,
2754                    cli_name(state->cli_change[i].client),
2755                    state->chptr->chname);
2756       continue;
2757     }
2758
2759     if ((state->cli_change[i].flag & MODE_ADD &&
2760          (state->cli_change[i].flag & member->status)) ||
2761         (state->cli_change[i].flag & MODE_DEL &&
2762          !(state->cli_change[i].flag & member->status)))
2763       continue; /* no change made, don't do anything */
2764
2765     /* see if the deop is allowed */
2766     if ((state->cli_change[i].flag & (MODE_DEL | MODE_CHANOP)) ==
2767         (MODE_DEL | MODE_CHANOP)) {
2768       /* prevent +k users from being deopped */
2769       if (IsChannelService(state->cli_change[i].client)) {
2770         if (state->flags & MODE_PARSE_FORCE) /* it was forced */
2771           sendto_opmask_butone(0, SNO_HACK4, "Deop of +k user on %H by %s",
2772                                state->chptr,
2773                                (IsServer(state->sptr) ? cli_name(state->sptr) :
2774                                 cli_name((cli_user(state->sptr))->server)));
2775
2776         else if (MyUser(state->sptr) && state->flags & MODE_PARSE_SET) {
2777           send_reply(state->sptr, ERR_ISCHANSERVICE,
2778                      cli_name(state->cli_change[i].client),
2779                      state->chptr->chname);
2780           continue;
2781         }
2782       }
2783
2784       /* check deop for local user */
2785       if (MyUser(state->sptr)) {
2786
2787         /* don't allow local opers to be deopped on local channels */
2788         if (MyUser(state->sptr) &&
2789             state->cli_change[i].client != state->sptr &&
2790             IsLocalChannel(state->chptr->chname) &&
2791             HasPriv(state->cli_change[i].client, PRIV_DEOP_LCHAN)) {
2792           send_reply(state->sptr, ERR_ISOPERLCHAN,
2793                      cli_name(state->cli_change[i].client),
2794                      state->chptr->chname);
2795           continue;
2796         }
2797
2798         /* don't allow to deop members with an op level that is <= our own level */
2799         if (state->sptr != state->cli_change[i].client          /* but allow to deop oneself */
2800                 && state->member
2801                 && OpLevel(member) <= OpLevel(state->member)) {
2802             int equal = (OpLevel(member) == OpLevel(state->member));
2803             send_reply(state->sptr, ERR_NOTLOWEROPLEVEL,
2804                        cli_name(state->cli_change[i].client),
2805                        state->chptr->chname,
2806                        OpLevel(state->member), OpLevel(member),
2807                        "deop", equal ? "the same" : "a higher");
2808           continue;
2809         }
2810       }
2811     }
2812
2813     /* set op-level of member being opped */
2814     if ((state->cli_change[i].flag & (MODE_ADD | MODE_CHANOP)) ==
2815         (MODE_ADD | MODE_CHANOP)) {
2816       /* If on a channel with upass set, someone with level x gives ops to someone else,
2817          then that person gets level x-1.  On other channels, where upass is not set,
2818          the level stays the same. */
2819       int level_increment = *state->chptr->mode.upass ? 1 : 0;
2820       /* Someone being opped by a server gets op-level 0 */
2821       int old_level = (state->member == NULL) ? -level_increment : OpLevel(state->member);
2822       SetOpLevel(member, old_level == MAXOPLEVEL ? MAXOPLEVEL : (old_level + level_increment));
2823     }
2824
2825     /* accumulate the change */
2826     modebuf_mode_client(state->mbuf, state->cli_change[i].flag,
2827                         state->cli_change[i].client);
2828
2829     /* actually effect the change */
2830     if (state->flags & MODE_PARSE_SET) {
2831       if (state->cli_change[i].flag & MODE_ADD) {
2832         member->status |= (state->cli_change[i].flag &
2833                            (MODE_CHANOP | MODE_VOICE));
2834         if (state->cli_change[i].flag & MODE_CHANOP)
2835           ClearDeopped(member);
2836       } else
2837         member->status &= ~(state->cli_change[i].flag &
2838                             (MODE_CHANOP | MODE_VOICE));
2839     }
2840   } /* for (i = 0; state->cli_change[i].flags; i++) */
2841 }
2842
2843 /*
2844  * Helper function to process the simple modes
2845  */
2846 static void
2847 mode_parse_mode(struct ParseState *state, int *flag_p)
2848 {
2849   /* If they're not an oper, they can't change modes */
2850   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2851     send_notoper(state);
2852     return;
2853   }
2854
2855   if (!state->mbuf)
2856     return;
2857
2858   if (state->dir == MODE_ADD) {
2859     state->add |= flag_p[0];
2860     state->del &= ~flag_p[0];
2861
2862     if (flag_p[0] & MODE_SECRET) {
2863       state->add &= ~MODE_PRIVATE;
2864       state->del |= MODE_PRIVATE;
2865     } else if (flag_p[0] & MODE_PRIVATE) {
2866       state->add &= ~MODE_SECRET;
2867       state->del |= MODE_SECRET;
2868     }
2869   } else {
2870     state->add &= ~flag_p[0];
2871     state->del |= flag_p[0];
2872   }
2873
2874   assert(0 == (state->add & state->del));
2875   assert((MODE_SECRET | MODE_PRIVATE) !=
2876          (state->add & (MODE_SECRET | MODE_PRIVATE)));
2877 }
2878
2879 /*
2880  * This routine is intended to parse MODE or OPMODE commands and effect the
2881  * changes (or just build the bounce buffer).  We pass the starting offset
2882  * as a 
2883  */
2884 int
2885 mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
2886            struct Channel *chptr, int parc, char *parv[], unsigned int flags,
2887            struct Membership* member)
2888 {
2889   static int chan_flags[] = {
2890     MODE_CHANOP,        'o',
2891     MODE_VOICE,         'v',
2892     MODE_PRIVATE,       'p',
2893     MODE_SECRET,        's',
2894     MODE_MODERATED,     'm',
2895     MODE_TOPICLIMIT,    't',
2896     MODE_INVITEONLY,    'i',
2897     MODE_NOPRIVMSGS,    'n',
2898     MODE_KEY,           'k',
2899     MODE_APASS,         'A',
2900     MODE_UPASS,         'u',
2901     MODE_BAN,           'b',
2902     MODE_LIMIT,         'l',
2903     MODE_REGONLY,       'r',
2904     MODE_ADD,           '+',
2905     MODE_DEL,           '-',
2906     0x0, 0x0
2907   };
2908   int i;
2909   int *flag_p;
2910   unsigned int t_mode;
2911   char *modestr;
2912   struct ParseState state;
2913
2914   assert(0 != cptr);
2915   assert(0 != sptr);
2916   assert(0 != chptr);
2917   assert(0 != parc);
2918   assert(0 != parv);
2919
2920   state.mbuf = mbuf;
2921   state.cptr = cptr;
2922   state.sptr = sptr;
2923   state.chptr = chptr;
2924   state.member = member;
2925   state.parc = parc;
2926   state.parv = parv;
2927   state.flags = flags;
2928   state.dir = MODE_ADD;
2929   state.done = 0;
2930   state.add = 0;
2931   state.del = 0;
2932   state.args_used = 0;
2933   state.max_args = MAXMODEPARAMS;
2934   state.numbans = 0;
2935
2936   for (i = 0; i < MAXPARA; i++) { /* initialize ops/voices arrays */
2937     state.banlist[i].next = 0;
2938     state.banlist[i].value.ban.banstr = 0;
2939     state.banlist[i].value.ban.who = 0;
2940     state.banlist[i].value.ban.when = 0;
2941     state.banlist[i].flags = 0;
2942     state.cli_change[i].flag = 0;
2943     state.cli_change[i].client = 0;
2944   }
2945
2946   modestr = state.parv[state.args_used++];
2947   state.parc--;
2948
2949   while (*modestr) {
2950     for (; *modestr; modestr++) {
2951       for (flag_p = chan_flags; flag_p[0]; flag_p += 2) /* look up flag */
2952         if (flag_p[1] == *modestr)
2953           break;
2954
2955       if (!flag_p[0]) { /* didn't find it?  complain and continue */
2956         if (MyUser(state.sptr))
2957           send_reply(state.sptr, ERR_UNKNOWNMODE, *modestr);
2958         continue;
2959       }
2960
2961       switch (*modestr) {
2962       case '+': /* switch direction to MODE_ADD */
2963       case '-': /* switch direction to MODE_DEL */
2964         state.dir = flag_p[0];
2965         break;
2966
2967       case 'l': /* deal with limits */
2968         mode_parse_limit(&state, flag_p);
2969         break;
2970
2971       case 'k': /* deal with keys */
2972         mode_parse_key(&state, flag_p);
2973         break;
2974
2975       case 'A': /* deal with Admin passes */
2976         mode_parse_apass(&state, flag_p);
2977         break;
2978
2979       case 'u': /* deal with user passes */
2980         mode_parse_upass(&state, flag_p);
2981         break;
2982
2983       case 'b': /* deal with bans */
2984         mode_parse_ban(&state, flag_p);
2985         break;
2986
2987       case 'o': /* deal with ops/voice */
2988       case 'v':
2989         mode_parse_client(&state, flag_p);
2990         break;
2991
2992       default: /* deal with other modes */
2993         mode_parse_mode(&state, flag_p);
2994         break;
2995       } /* switch (*modestr) */
2996     } /* for (; *modestr; modestr++) */
2997
2998     if (state.flags & MODE_PARSE_BURST)
2999       break; /* don't interpret any more arguments */
3000
3001     if (state.parc > 0) { /* process next argument in string */
3002       modestr = state.parv[state.args_used++];
3003       state.parc--;
3004
3005       /* is it a TS? */
3006       if (IsServer(state.sptr) && !state.parc && IsDigit(*modestr)) {
3007         time_t recv_ts;
3008
3009         if (!(state.flags & MODE_PARSE_SET))      /* don't set earlier TS if */
3010           break;                     /* we're then going to bounce the mode! */
3011
3012         recv_ts = atoi(modestr);
3013
3014         if (recv_ts && recv_ts < state.chptr->creationtime)
3015           state.chptr->creationtime = recv_ts; /* respect earlier TS */
3016
3017         break; /* break out of while loop */
3018       } else if (state.flags & MODE_PARSE_STRICT ||
3019                  (MyUser(state.sptr) && state.max_args <= 0)) {
3020         state.parc++; /* we didn't actually gobble the argument */
3021         state.args_used--;
3022         break; /* break out of while loop */
3023       }
3024     }
3025   } /* while (*modestr) */
3026
3027   /*
3028    * the rest of the function finishes building resultant MODEs; if the
3029    * origin isn't a member or an oper, skip it.
3030    */
3031   if (!state.mbuf || state.flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER))
3032     return state.args_used; /* tell our parent how many args we gobbled */
3033
3034   t_mode = state.chptr->mode.mode;
3035
3036   if (state.del & t_mode) { /* delete any modes to be deleted... */
3037     modebuf_mode(state.mbuf, MODE_DEL | (state.del & t_mode));
3038
3039     t_mode &= ~state.del;
3040   }
3041   if (state.add & ~t_mode) { /* add any modes to be added... */
3042     modebuf_mode(state.mbuf, MODE_ADD | (state.add & ~t_mode));
3043
3044     t_mode |= state.add;
3045   }
3046
3047   if (state.flags & MODE_PARSE_SET) { /* set the channel modes */
3048     if ((state.chptr->mode.mode & MODE_INVITEONLY) &&
3049         !(t_mode & MODE_INVITEONLY))
3050       mode_invite_clear(state.chptr);
3051
3052     state.chptr->mode.mode = t_mode;
3053   }
3054
3055   if (state.flags & MODE_PARSE_WIPEOUT) {
3056     if (state.chptr->mode.limit && !(state.done & DONE_LIMIT))
3057       modebuf_mode_uint(state.mbuf, MODE_DEL | MODE_LIMIT,
3058                         state.chptr->mode.limit);
3059     if (*state.chptr->mode.key && !(state.done & DONE_KEY))
3060       modebuf_mode_string(state.mbuf, MODE_DEL | MODE_KEY,
3061                           state.chptr->mode.key, 0);
3062     if (*state.chptr->mode.upass && !(state.done & DONE_UPASS))
3063       modebuf_mode_string(state.mbuf, MODE_DEL | MODE_UPASS,
3064                           state.chptr->mode.upass, 0);
3065     if (*state.chptr->mode.apass && !(state.done & DONE_APASS))
3066       modebuf_mode_string(state.mbuf, MODE_DEL | MODE_APASS,
3067                           state.chptr->mode.apass, 0);
3068   }
3069
3070   if (state.done & DONE_BANCLEAN) /* process bans */
3071     mode_process_bans(&state);
3072
3073   /* process client changes */
3074   if (state.cli_change[0].flag)
3075     mode_process_clients(&state);
3076
3077   return state.args_used; /* tell our parent how many args we gobbled */
3078 }
3079
3080 /*
3081  * Initialize a join buffer
3082  */
3083 void
3084 joinbuf_init(struct JoinBuf *jbuf, struct Client *source,
3085              struct Client *connect, unsigned int type, char *comment,
3086              time_t create)
3087 {
3088   int i;
3089
3090   assert(0 != jbuf);
3091   assert(0 != source);
3092   assert(0 != connect);
3093
3094   jbuf->jb_source = source; /* just initialize struct JoinBuf */
3095   jbuf->jb_connect = connect;
3096   jbuf->jb_type = type;
3097   jbuf->jb_comment = comment;
3098   jbuf->jb_create = create;
3099   jbuf->jb_count = 0;
3100   jbuf->jb_strlen = (((type == JOINBUF_TYPE_JOIN ||
3101                        type == JOINBUF_TYPE_PART ||
3102                        type == JOINBUF_TYPE_PARTALL) ?
3103                       STARTJOINLEN : STARTCREATELEN) +
3104                      (comment ? strlen(comment) + 2 : 0));
3105
3106   for (i = 0; i < MAXJOINARGS; i++)
3107     jbuf->jb_channels[i] = 0;
3108 }
3109
3110 /*
3111  * Add a channel to the join buffer
3112  */
3113 void
3114 joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
3115 {
3116   unsigned int len;
3117   int is_local;
3118
3119   assert(0 != jbuf);
3120
3121   if (!chan) {
3122     if (jbuf->jb_type == JOINBUF_TYPE_JOIN)
3123       sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect, "0");
3124
3125     return;
3126   }
3127
3128   is_local = IsLocalChannel(chan->chname);
3129
3130   if (jbuf->jb_type == JOINBUF_TYPE_PART ||
3131       jbuf->jb_type == JOINBUF_TYPE_PARTALL) {
3132     /* Send notification to channel */
3133     if (!(flags & CHFL_ZOMBIE))
3134       sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_PART, chan, NULL,
3135                                 (flags & CHFL_BANNED || !jbuf->jb_comment) ?
3136                                 ":%H" : "%H :%s", chan, jbuf->jb_comment);
3137     else if (MyUser(jbuf->jb_source))
3138       sendcmdto_one(jbuf->jb_source, CMD_PART, jbuf->jb_source,
3139                     (flags & CHFL_BANNED || !jbuf->jb_comment) ?
3140                     ":%H" : "%H :%s", chan, jbuf->jb_comment);
3141     /* XXX: Shouldn't we send a PART here anyway? */
3142     /* to users on the channel?  Why?  From their POV, the user isn't on
3143      * the channel anymore anyway.  We don't send to servers until below,
3144      * when we gang all the channel parts together.  Note that this is
3145      * exactly the same logic, albeit somewhat more concise, as was in
3146      * the original m_part.c */
3147
3148     if (jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
3149         is_local) /* got to remove user here */
3150       remove_user_from_channel(jbuf->jb_source, chan);
3151   } else {
3152     /* Add user to channel */
3153     add_user_to_channel(chan, jbuf->jb_source, flags, 0);
3154
3155     /* send notification to all servers */
3156     if (jbuf->jb_type != JOINBUF_TYPE_CREATE && !is_local)
3157       sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect,
3158                             "%H %Tu", chan, chan->creationtime);
3159
3160     /* Send the notification to the channel */
3161     sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_JOIN, chan, NULL, ":%H", chan);
3162
3163     /* send an op, too, if needed */
3164     if (!MyUser(jbuf->jb_source) && jbuf->jb_type == JOINBUF_TYPE_CREATE)
3165       sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_MODE, chan, NULL, "%H +o %C",
3166                                 chan, jbuf->jb_source);
3167   }
3168
3169   if (jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
3170       jbuf->jb_type == JOINBUF_TYPE_JOIN || is_local)
3171     return; /* don't send to remote */
3172
3173   /* figure out if channel name will cause buffer to be overflowed */
3174   len = chan ? strlen(chan->chname) + 1 : 2;
3175   if (jbuf->jb_strlen + len > BUFSIZE)
3176     joinbuf_flush(jbuf);
3177
3178   /* add channel to list of channels to send and update counts */
3179   jbuf->jb_channels[jbuf->jb_count++] = chan;
3180   jbuf->jb_strlen += len;
3181
3182   /* if we've used up all slots, flush */
3183   if (jbuf->jb_count >= MAXJOINARGS)
3184     joinbuf_flush(jbuf);
3185 }
3186
3187 /*
3188  * Flush the channel list to remote servers
3189  */
3190 int
3191 joinbuf_flush(struct JoinBuf *jbuf)
3192 {
3193   char chanlist[BUFSIZE];
3194   int chanlist_i = 0;
3195   int i;
3196
3197   if (!jbuf->jb_count || jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
3198       jbuf->jb_type == JOINBUF_TYPE_JOIN)
3199     return 0; /* no joins to process */
3200
3201   for (i = 0; i < jbuf->jb_count; i++) { /* build channel list */
3202     build_string(chanlist, &chanlist_i,
3203                  jbuf->jb_channels[i] ? jbuf->jb_channels[i]->chname : "0", 0,
3204                  i == 0 ? '\0' : ',');
3205     if (JOINBUF_TYPE_PART == jbuf->jb_type)
3206       /* Remove user from channel */
3207       remove_user_from_channel(jbuf->jb_source, jbuf->jb_channels[i]);
3208
3209     jbuf->jb_channels[i] = 0; /* mark slot empty */
3210   }
3211
3212   jbuf->jb_count = 0; /* reset base counters */
3213   jbuf->jb_strlen = ((jbuf->jb_type == JOINBUF_TYPE_PART ?
3214                       STARTJOINLEN : STARTCREATELEN) +
3215                      (jbuf->jb_comment ? strlen(jbuf->jb_comment) + 2 : 0));
3216
3217   /* and send the appropriate command */
3218   switch (jbuf->jb_type) {
3219   case JOINBUF_TYPE_CREATE:
3220     sendcmdto_serv_butone(jbuf->jb_source, CMD_CREATE, jbuf->jb_connect,
3221                           "%s %Tu", chanlist, jbuf->jb_create);
3222     break;
3223
3224   case JOINBUF_TYPE_PART:
3225     sendcmdto_serv_butone(jbuf->jb_source, CMD_PART, jbuf->jb_connect,
3226                           jbuf->jb_comment ? "%s :%s" : "%s", chanlist,
3227                           jbuf->jb_comment);
3228     break;
3229   }
3230
3231   return 0;
3232 }
3233
3234 /* Returns TRUE (1) if client is invited, FALSE (0) if not */
3235 int IsInvited(struct Client* cptr, struct Channel* chptr)
3236 {
3237   struct SLink *lp;
3238
3239   for (lp = (cli_user(cptr))->invited; lp; lp = lp->next)
3240     if (lp->value.chptr == chptr)
3241       return 1;
3242   return 0;
3243 }