Cleanup code so it builds with C++ again
[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, const char *str1,
1466              const char *str2, char c)
1467 {
1468   if (c)
1469     strptr[(*strptr_i)++] = c;
1470
1471   while (*str1)
1472     strptr[(*strptr_i)++] = *(str1++);
1473
1474   if (str2)
1475     while (*str2)
1476       strptr[(*strptr_i)++] = *(str2++);
1477
1478   strptr[(*strptr_i)] = '\0';
1479 }
1480
1481 /*
1482  * This is the workhorse of our ModeBuf suite; this actually generates the
1483  * output MODE commands, HACK notices, or whatever.  It's pretty complicated.
1484  */
1485 static int
1486 modebuf_flush_int(struct ModeBuf *mbuf, int all)
1487 {
1488   /* we only need the flags that don't take args right now */
1489   static int flags[] = {
1490 /*  MODE_CHANOP,        'o', */
1491 /*  MODE_VOICE,         'v', */
1492     MODE_PRIVATE,       'p',
1493     MODE_SECRET,        's',
1494     MODE_MODERATED,     'm',
1495     MODE_TOPICLIMIT,    't',
1496     MODE_INVITEONLY,    'i',
1497     MODE_NOPRIVMSGS,    'n',
1498     MODE_REGONLY,       'r',
1499 /*  MODE_KEY,           'k', */
1500 /*  MODE_BAN,           'b', */
1501 /*  MODE_LIMIT,         'l', */
1502 /*  MODE_APASS,         'A', */
1503 /*  MODE_UPASS,         'u', */
1504     0x0, 0x0
1505   };
1506   int i;
1507   int *flag_p;
1508
1509   struct Client *app_source; /* where the MODE appears to come from */
1510
1511   char addbuf[20]; /* accumulates +psmtin, etc. */
1512   int addbuf_i = 0;
1513   char rembuf[20]; /* accumulates -psmtin, etc. */
1514   int rembuf_i = 0;
1515   char *bufptr; /* we make use of indirection to simplify the code */
1516   int *bufptr_i;
1517
1518   char addstr[BUFSIZE]; /* accumulates MODE parameters to add */
1519   int addstr_i;
1520   char remstr[BUFSIZE]; /* accumulates MODE parameters to remove */
1521   int remstr_i;
1522   char *strptr; /* more indirection to simplify the code */
1523   int *strptr_i;
1524
1525   int totalbuflen = BUFSIZE - 200; /* fuzz factor -- don't overrun buffer! */
1526   int tmp;
1527
1528   char limitbuf[20]; /* convert limits to strings */
1529
1530   unsigned int limitdel = MODE_LIMIT;
1531
1532   assert(0 != mbuf);
1533
1534   /* If the ModeBuf is empty, we have nothing to do */
1535   if (mbuf->mb_add == 0 && mbuf->mb_rem == 0 && mbuf->mb_count == 0)
1536     return 0;
1537
1538   /* Ok, if we were given the OPMODE flag, or its a server, hide the source.
1539    */
1540   if (mbuf->mb_dest & MODEBUF_DEST_OPMODE || IsServer(mbuf->mb_source))
1541     app_source = &me;
1542   else
1543     app_source = mbuf->mb_source;
1544
1545   /*
1546    * Account for user we're bouncing; we have to get it in on the first
1547    * bounced MODE, or we could have problems
1548    */
1549   if (mbuf->mb_dest & MODEBUF_DEST_DEOP)
1550     totalbuflen -= 6; /* numeric nick == 5, plus one space */
1551
1552   /* Calculate the simple flags */
1553   for (flag_p = flags; flag_p[0]; flag_p += 2) {
1554     if (*flag_p & mbuf->mb_add)
1555       addbuf[addbuf_i++] = flag_p[1];
1556     else if (*flag_p & mbuf->mb_rem)
1557       rembuf[rembuf_i++] = flag_p[1];
1558   }
1559
1560   /* Now go through the modes with arguments... */
1561   for (i = 0; i < mbuf->mb_count; i++) {
1562     if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
1563       bufptr = addbuf;
1564       bufptr_i = &addbuf_i;
1565     } else {
1566       bufptr = rembuf;
1567       bufptr_i = &rembuf_i;
1568     }
1569
1570     if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE)) {
1571       tmp = strlen(cli_name(MB_CLIENT(mbuf, i)));
1572
1573       if ((totalbuflen - IRCD_MAX(5, tmp)) <= 0) /* don't overflow buffer */
1574         MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1575       else {
1576         bufptr[(*bufptr_i)++] = MB_TYPE(mbuf, i) & MODE_CHANOP ? 'o' : 'v';
1577         totalbuflen -= IRCD_MAX(5, tmp) + 1;
1578       }
1579     } else if (MB_TYPE(mbuf, i) & (MODE_BAN | MODE_APASS | MODE_UPASS)) {
1580       tmp = strlen(MB_STRING(mbuf, i));
1581
1582       if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
1583         MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1584       else {
1585         char mode_char;
1586         switch(MB_TYPE(mbuf, i) & (MODE_BAN | MODE_APASS | MODE_UPASS))
1587         {
1588           case MODE_APASS:
1589             mode_char = 'A';
1590             break;
1591           case MODE_UPASS:
1592             mode_char = 'u';
1593             break;
1594           default:
1595             mode_char = 'b';
1596             break;
1597         }
1598         bufptr[(*bufptr_i)++] = mode_char;
1599         totalbuflen -= tmp + 1;
1600       }
1601     } else if (MB_TYPE(mbuf, i) & MODE_KEY) {
1602       tmp = (mbuf->mb_dest & MODEBUF_DEST_NOKEY ? 1 :
1603              strlen(MB_STRING(mbuf, i)));
1604
1605       if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
1606         MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1607       else {
1608         bufptr[(*bufptr_i)++] = 'k';
1609         totalbuflen -= tmp + 1;
1610       }
1611     } else if (MB_TYPE(mbuf, i) & MODE_LIMIT) {
1612       /* if it's a limit, we also format the number */
1613       ircd_snprintf(0, limitbuf, sizeof(limitbuf), "%u", MB_UINT(mbuf, i));
1614
1615       tmp = strlen(limitbuf);
1616
1617       if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
1618         MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1619       else {
1620         bufptr[(*bufptr_i)++] = 'l';
1621         totalbuflen -= tmp + 1;
1622       }
1623     }
1624   }
1625
1626   /* terminate the mode strings */
1627   addbuf[addbuf_i] = '\0';
1628   rembuf[rembuf_i] = '\0';
1629
1630   /* If we're building a user visible MODE or HACK... */
1631   if (mbuf->mb_dest & (MODEBUF_DEST_CHANNEL | MODEBUF_DEST_HACK2 |
1632                        MODEBUF_DEST_HACK3   | MODEBUF_DEST_HACK4 |
1633                        MODEBUF_DEST_LOG)) {
1634     /* Set up the parameter strings */
1635     addstr[0] = '\0';
1636     addstr_i = 0;
1637     remstr[0] = '\0';
1638     remstr_i = 0;
1639
1640     for (i = 0; i < mbuf->mb_count; i++) {
1641       if (MB_TYPE(mbuf, i) & MODE_SAVE)
1642         continue;
1643
1644       if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
1645         strptr = addstr;
1646         strptr_i = &addstr_i;
1647       } else {
1648         strptr = remstr;
1649         strptr_i = &remstr_i;
1650       }
1651
1652       /* deal with clients... */
1653       if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE))
1654         build_string(strptr, strptr_i, cli_name(MB_CLIENT(mbuf, i)), 0, ' ');
1655
1656       /* deal with bans... */
1657       else if (MB_TYPE(mbuf, i) & MODE_BAN)
1658         build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
1659
1660       /* deal with keys... */
1661       else if (MB_TYPE(mbuf, i) & MODE_KEY)
1662         build_string(strptr, strptr_i, mbuf->mb_dest & MODEBUF_DEST_NOKEY ?
1663                      "*" : MB_STRING(mbuf, i), 0, ' ');
1664
1665       /* deal with invisible passwords */
1666       else if (MB_TYPE(mbuf, i) & (MODE_APASS | MODE_UPASS))
1667         build_string(strptr, strptr_i, "*", 0, ' ');
1668
1669       /*
1670        * deal with limit; note we cannot include the limit parameter if we're
1671        * removing it
1672        */
1673       else if ((MB_TYPE(mbuf, i) & (MODE_ADD | MODE_LIMIT)) ==
1674                (MODE_ADD | MODE_LIMIT))
1675         build_string(strptr, strptr_i, limitbuf, 0, ' ');
1676     }
1677
1678     /* send the messages off to their destination */
1679     if (mbuf->mb_dest & MODEBUF_DEST_HACK2)
1680       sendto_opmask_butone(0, SNO_HACK2, "HACK(2): %s MODE %s %s%s%s%s%s%s "
1681                            "[%Tu]",
1682                            cli_name(feature_bool(FEAT_HIS_SNOTICES) ?
1683                                     mbuf->mb_source : app_source),
1684                            mbuf->mb_channel->chname,
1685                            rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1686                            addbuf, remstr, addstr,
1687                            mbuf->mb_channel->creationtime);
1688
1689     if (mbuf->mb_dest & MODEBUF_DEST_HACK3)
1690       sendto_opmask_butone(0, SNO_HACK3, "BOUNCE or HACK(3): %s MODE %s "
1691                            "%s%s%s%s%s%s [%Tu]",
1692                            cli_name(feature_bool(FEAT_HIS_SNOTICES) ? 
1693                                     mbuf->mb_source : app_source),
1694                            mbuf->mb_channel->chname, rembuf_i ? "-" : "",
1695                            rembuf, addbuf_i ? "+" : "", addbuf, remstr, addstr,
1696                            mbuf->mb_channel->creationtime);
1697
1698     if (mbuf->mb_dest & MODEBUF_DEST_HACK4)
1699       sendto_opmask_butone(0, SNO_HACK4, "HACK(4): %s MODE %s %s%s%s%s%s%s "
1700                            "[%Tu]",
1701 #ifdef HEAD_IN_SAND_SNOTICES
1702                            cli_name(mbuf->mb_source),
1703 #else
1704                            cli_name(app_source),
1705 #endif
1706                            mbuf->mb_channel->chname,
1707                            rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1708                            addbuf, remstr, addstr,
1709                            mbuf->mb_channel->creationtime);
1710
1711     if (mbuf->mb_dest & MODEBUF_DEST_LOG)
1712       log_write(LS_OPERMODE, L_INFO, LOG_NOSNOTICE,
1713                 "%#C OPMODE %H %s%s%s%s%s%s", mbuf->mb_source,
1714                 mbuf->mb_channel, rembuf_i ? "-" : "", rembuf,
1715                 addbuf_i ? "+" : "", addbuf, remstr, addstr);
1716
1717     if (mbuf->mb_dest & MODEBUF_DEST_CHANNEL)
1718       sendcmdto_channel_butserv_butone(app_source, CMD_MODE, mbuf->mb_channel, NULL,
1719                                 "%H %s%s%s%s%s%s", mbuf->mb_channel,
1720                                 rembuf_i ? "-" : "", rembuf,
1721                                 addbuf_i ? "+" : "", addbuf, remstr, addstr);
1722   }
1723
1724   /* Now are we supposed to propagate to other servers? */
1725   if (mbuf->mb_dest & MODEBUF_DEST_SERVER) {
1726     /* set up parameter string */
1727     addstr[0] = '\0';
1728     addstr_i = 0;
1729     remstr[0] = '\0';
1730     remstr_i = 0;
1731
1732     /*
1733      * limit is supressed if we're removing it; we have to figure out which
1734      * direction is the direction for it to be removed, though...
1735      */
1736     limitdel |= (mbuf->mb_dest & MODEBUF_DEST_HACK2) ? MODE_DEL : MODE_ADD;
1737
1738     for (i = 0; i < mbuf->mb_count; i++) {
1739       if (MB_TYPE(mbuf, i) & MODE_SAVE)
1740         continue;
1741
1742       if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
1743         strptr = addstr;
1744         strptr_i = &addstr_i;
1745       } else {
1746         strptr = remstr;
1747         strptr_i = &remstr_i;
1748       }
1749
1750       /* deal with modes that take clients */
1751       if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE))
1752         build_string(strptr, strptr_i, NumNick(MB_CLIENT(mbuf, i)), ' ');
1753
1754       /* deal with modes that take strings */
1755       else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN | MODE_APASS | MODE_UPASS))
1756         build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
1757
1758       /*
1759        * deal with the limit.  Logic here is complicated; if HACK2 is set,
1760        * we're bouncing the mode, so sense is reversed, and we have to
1761        * include the original limit if it looks like it's being removed
1762        */
1763       else if ((MB_TYPE(mbuf, i) & limitdel) == limitdel)
1764         build_string(strptr, strptr_i, limitbuf, 0, ' ');
1765     }
1766
1767     /* we were told to deop the source */
1768     if (mbuf->mb_dest & MODEBUF_DEST_DEOP) {
1769       addbuf[addbuf_i++] = 'o'; /* remember, sense is reversed */
1770       addbuf[addbuf_i] = '\0'; /* terminate the string... */
1771       build_string(addstr, &addstr_i, NumNick(mbuf->mb_source), ' ');
1772
1773       /* mark that we've done this, so we don't do it again */
1774       mbuf->mb_dest &= ~MODEBUF_DEST_DEOP;
1775     }
1776
1777     if (mbuf->mb_dest & MODEBUF_DEST_OPMODE) {
1778       /* If OPMODE was set, we're propagating the mode as an OPMODE message */
1779       sendcmdto_serv_butone(mbuf->mb_source, CMD_OPMODE, mbuf->mb_connect,
1780                             "%H %s%s%s%s%s%s", mbuf->mb_channel,
1781                             rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1782                             addbuf, remstr, addstr);
1783     } else if (mbuf->mb_dest & MODEBUF_DEST_BOUNCE) {
1784       /*
1785        * If HACK2 was set, we're bouncing; we send the MODE back to the
1786        * connection we got it from with the senses reversed and a TS of 0;
1787        * origin is us
1788        */
1789       sendcmdto_one(&me, CMD_MODE, mbuf->mb_connect, "%H %s%s%s%s%s%s %Tu",
1790                     mbuf->mb_channel, addbuf_i ? "-" : "", addbuf,
1791                     rembuf_i ? "+" : "", rembuf, addstr, remstr,
1792                     mbuf->mb_channel->creationtime);
1793     } else {
1794       /*
1795        * We're propagating a normal MODE command to the rest of the network;
1796        * we send the actual channel TS unless this is a HACK3 or a HACK4
1797        */
1798       if (IsServer(mbuf->mb_source))
1799         sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
1800                               "%H %s%s%s%s%s%s %Tu", mbuf->mb_channel,
1801                               rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1802                               addbuf, remstr, addstr,
1803                               (mbuf->mb_dest & MODEBUF_DEST_HACK4) ? 0 :
1804                               mbuf->mb_channel->creationtime);
1805       else
1806         sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
1807                               "%H %s%s%s%s%s%s", mbuf->mb_channel,
1808                               rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1809                               addbuf, remstr, addstr);
1810     }
1811   }
1812
1813   /* We've drained the ModeBuf... */
1814   mbuf->mb_add = 0;
1815   mbuf->mb_rem = 0;
1816   mbuf->mb_count = 0;
1817
1818   /* reinitialize the mode-with-arg slots */
1819   for (i = 0; i < MAXMODEPARAMS; i++) {
1820     /* If we saved any, pack them down */
1821     if (MB_TYPE(mbuf, i) & MODE_SAVE) {
1822       mbuf->mb_modeargs[mbuf->mb_count] = mbuf->mb_modeargs[i];
1823       MB_TYPE(mbuf, mbuf->mb_count) &= ~MODE_SAVE; /* don't save anymore */
1824
1825       if (mbuf->mb_count++ == i) /* don't overwrite our hard work */
1826         continue;
1827     } else if (MB_TYPE(mbuf, i) & MODE_FREE)
1828       MyFree(MB_STRING(mbuf, i)); /* free string if needed */
1829
1830     MB_TYPE(mbuf, i) = 0;
1831     MB_UINT(mbuf, i) = 0;
1832   }
1833
1834   /* If we're supposed to flush it all, do so--all hail tail recursion */
1835   if (all && mbuf->mb_count)
1836     return modebuf_flush_int(mbuf, 1);
1837
1838   return 0;
1839 }
1840
1841 /*
1842  * This routine just initializes a ModeBuf structure with the information
1843  * needed and the options given.
1844  */
1845 void
1846 modebuf_init(struct ModeBuf *mbuf, struct Client *source,
1847              struct Client *connect, struct Channel *chan, unsigned int dest)
1848 {
1849   int i;
1850
1851   assert(0 != mbuf);
1852   assert(0 != source);
1853   assert(0 != chan);
1854   assert(0 != dest);
1855
1856   mbuf->mb_add = 0;
1857   mbuf->mb_rem = 0;
1858   mbuf->mb_source = source;
1859   mbuf->mb_connect = connect;
1860   mbuf->mb_channel = chan;
1861   mbuf->mb_dest = dest;
1862   mbuf->mb_count = 0;
1863
1864   /* clear each mode-with-parameter slot */
1865   for (i = 0; i < MAXMODEPARAMS; i++) {
1866     MB_TYPE(mbuf, i) = 0;
1867     MB_UINT(mbuf, i) = 0;
1868   }
1869 }
1870
1871 /*
1872  * This routine simply adds modes to be added or deleted; do a binary OR
1873  * with either MODE_ADD or MODE_DEL
1874  */
1875 void
1876 modebuf_mode(struct ModeBuf *mbuf, unsigned int mode)
1877 {
1878   assert(0 != mbuf);
1879   assert(0 != (mode & (MODE_ADD | MODE_DEL)));
1880
1881   mode &= (MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET | MODE_MODERATED |
1882            MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS | MODE_REGONLY);
1883
1884   if (!(mode & ~(MODE_ADD | MODE_DEL))) /* don't add empty modes... */
1885     return;
1886
1887   if (mode & MODE_ADD) {
1888     mbuf->mb_rem &= ~mode;
1889     mbuf->mb_add |= mode;
1890   } else {
1891     mbuf->mb_add &= ~mode;
1892     mbuf->mb_rem |= mode;
1893   }
1894 }
1895
1896 /*
1897  * This routine adds a mode to be added or deleted that takes a unsigned
1898  * int parameter; mode may *only* be the relevant mode flag ORed with one
1899  * of MODE_ADD or MODE_DEL
1900  */
1901 void
1902 modebuf_mode_uint(struct ModeBuf *mbuf, unsigned int mode, unsigned int uint)
1903 {
1904   assert(0 != mbuf);
1905   assert(0 != (mode & (MODE_ADD | MODE_DEL)));
1906
1907   MB_TYPE(mbuf, mbuf->mb_count) = mode;
1908   MB_UINT(mbuf, mbuf->mb_count) = uint;
1909
1910   /* when we've reached the maximal count, flush the buffer */
1911   if (++mbuf->mb_count >=
1912       (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
1913     modebuf_flush_int(mbuf, 0);
1914 }
1915
1916 /*
1917  * This routine adds a mode to be added or deleted that takes a string
1918  * parameter; mode may *only* be the relevant mode flag ORed with one of
1919  * MODE_ADD or MODE_DEL
1920  */
1921 void
1922 modebuf_mode_string(struct ModeBuf *mbuf, unsigned int mode, char *string,
1923                     int free)
1924 {
1925   assert(0 != mbuf);
1926   assert(0 != (mode & (MODE_ADD | MODE_DEL)));
1927
1928   MB_TYPE(mbuf, mbuf->mb_count) = mode | (free ? MODE_FREE : 0);
1929   MB_STRING(mbuf, mbuf->mb_count) = string;
1930
1931   /* when we've reached the maximal count, flush the buffer */
1932   if (++mbuf->mb_count >=
1933       (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
1934     modebuf_flush_int(mbuf, 0);
1935 }
1936
1937 /*
1938  * This routine adds a mode to be added or deleted that takes a client
1939  * parameter; mode may *only* be the relevant mode flag ORed with one of
1940  * MODE_ADD or MODE_DEL
1941  */
1942 void
1943 modebuf_mode_client(struct ModeBuf *mbuf, unsigned int mode,
1944                     struct Client *client)
1945 {
1946   assert(0 != mbuf);
1947   assert(0 != (mode & (MODE_ADD | MODE_DEL)));
1948
1949   MB_TYPE(mbuf, mbuf->mb_count) = mode;
1950   MB_CLIENT(mbuf, mbuf->mb_count) = client;
1951
1952   /* when we've reached the maximal count, flush the buffer */
1953   if (++mbuf->mb_count >=
1954       (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
1955     modebuf_flush_int(mbuf, 0);
1956 }
1957
1958 /*
1959  * This is the exported binding for modebuf_flush()
1960  */
1961 int
1962 modebuf_flush(struct ModeBuf *mbuf)
1963 {
1964   return modebuf_flush_int(mbuf, 1);
1965 }
1966
1967 /*
1968  * This extracts the simple modes contained in mbuf
1969  */
1970 void
1971 modebuf_extract(struct ModeBuf *mbuf, char *buf)
1972 {
1973   static int flags[] = {
1974 /*  MODE_CHANOP,        'o', */
1975 /*  MODE_VOICE,         'v', */
1976     MODE_PRIVATE,       'p',
1977     MODE_SECRET,        's',
1978     MODE_MODERATED,     'm',
1979     MODE_TOPICLIMIT,    't',
1980     MODE_INVITEONLY,    'i',
1981     MODE_NOPRIVMSGS,    'n',
1982     MODE_KEY,           'k',
1983     MODE_APASS,         'A',
1984     MODE_UPASS,         'u',
1985 /*  MODE_BAN,           'b', */
1986     MODE_LIMIT,         'l',
1987     MODE_REGONLY,       'r',
1988     0x0, 0x0
1989   };
1990   unsigned int add;
1991   int i, bufpos = 0, len;
1992   int *flag_p;
1993   char *key = 0, limitbuf[20];
1994   char *apass = 0, *upass = 0;
1995
1996   assert(0 != mbuf);
1997   assert(0 != buf);
1998
1999   buf[0] = '\0';
2000
2001   add = mbuf->mb_add;
2002
2003   for (i = 0; i < mbuf->mb_count; i++) { /* find keys and limits */
2004     if (MB_TYPE(mbuf, i) & MODE_ADD) {
2005       add |= MB_TYPE(mbuf, i) & (MODE_KEY | MODE_LIMIT | MODE_APASS | MODE_UPASS);
2006
2007       if (MB_TYPE(mbuf, i) & MODE_KEY) /* keep strings */
2008         key = MB_STRING(mbuf, i);
2009       else if (MB_TYPE(mbuf, i) & MODE_LIMIT)
2010         ircd_snprintf(0, limitbuf, sizeof(limitbuf), "%u", MB_UINT(mbuf, i));
2011       else if (MB_TYPE(mbuf, i) & MODE_UPASS)
2012         upass = MB_STRING(mbuf, i);
2013       else if (MB_TYPE(mbuf, i) & MODE_APASS)
2014         apass = MB_STRING(mbuf, i);
2015     }
2016   }
2017
2018   if (!add)
2019     return;
2020
2021   buf[bufpos++] = '+'; /* start building buffer */
2022
2023   for (flag_p = flags; flag_p[0]; flag_p += 2)
2024     if (*flag_p & add)
2025       buf[bufpos++] = flag_p[1];
2026
2027   for (i = 0, len = bufpos; i < len; i++) {
2028     if (buf[i] == 'k')
2029       build_string(buf, &bufpos, key, 0, ' ');
2030     else if (buf[i] == 'l')
2031       build_string(buf, &bufpos, limitbuf, 0, ' ');
2032     else if (buf[i] == 'u')
2033       build_string(buf, &bufpos, upass, 0, ' ');
2034     else if (buf[i] == 'A')
2035       build_string(buf, &bufpos, apass, 0, ' ');
2036   }
2037
2038   buf[bufpos] = '\0';
2039
2040   return;
2041 }
2042
2043 /*
2044  * Simple function to invalidate bans
2045  */
2046 void
2047 mode_ban_invalidate(struct Channel *chan)
2048 {
2049   struct Membership *member;
2050
2051   for (member = chan->members; member; member = member->next_member)
2052     ClearBanValid(member);
2053 }
2054
2055 /*
2056  * Simple function to drop invite structures
2057  */
2058 void
2059 mode_invite_clear(struct Channel *chan)
2060 {
2061   while (chan->invites)
2062     del_invite(chan->invites->value.cptr, chan);
2063 }
2064
2065 /* What we've done for mode_parse so far... */
2066 #define DONE_LIMIT      0x01    /* We've set the limit */
2067 #define DONE_KEY        0x02    /* We've set the key */
2068 #define DONE_BANLIST    0x04    /* We've sent the ban list */
2069 #define DONE_NOTOPER    0x08    /* We've sent a "Not oper" error */
2070 #define DONE_BANCLEAN   0x10    /* We've cleaned bans... */
2071 #define DONE_UPASS      0x20    /* We've set user pass */
2072 #define DONE_APASS      0x40    /* We've set admin pass */
2073
2074 struct ParseState {
2075   struct ModeBuf *mbuf;
2076   struct Client *cptr;
2077   struct Client *sptr;
2078   struct Channel *chptr;
2079   struct Membership *member;
2080   int parc;
2081   char **parv;
2082   unsigned int flags;
2083   unsigned int dir;
2084   unsigned int done;
2085   unsigned int add;
2086   unsigned int del;
2087   int args_used;
2088   int max_args;
2089   int numbans;
2090   struct SLink banlist[MAXPARA];
2091   struct {
2092     unsigned int flag;
2093     struct Client *client;
2094   } cli_change[MAXPARA];
2095 };
2096
2097 /*
2098  * Here's a helper function to deal with sending along "Not oper" or
2099  * "Not member" messages
2100  */
2101 static void
2102 send_notoper(struct ParseState *state)
2103 {
2104   if (state->done & DONE_NOTOPER)
2105     return;
2106
2107   send_reply(state->sptr, (state->flags & MODE_PARSE_NOTOPER) ?
2108              ERR_CHANOPRIVSNEEDED : ERR_NOTONCHANNEL, state->chptr->chname);
2109
2110   state->done |= DONE_NOTOPER;
2111 }
2112
2113 /*
2114  * Helper function to convert limits
2115  */
2116 static void
2117 mode_parse_limit(struct ParseState *state, int *flag_p)
2118 {
2119   unsigned int t_limit;
2120
2121   if (state->dir == MODE_ADD) { /* convert arg only if adding limit */
2122     if (MyUser(state->sptr) && state->max_args <= 0) /* too many args? */
2123       return;
2124
2125     if (state->parc <= 0) { /* warn if not enough args */
2126       if (MyUser(state->sptr))
2127         need_more_params(state->sptr, "MODE +l");
2128       return;
2129     }
2130
2131     t_limit = strtoul(state->parv[state->args_used++], 0, 10); /* grab arg */
2132     state->parc--;
2133     state->max_args--;
2134
2135     if (!(state->flags & MODE_PARSE_WIPEOUT) &&
2136         (!t_limit || t_limit == state->chptr->mode.limit))
2137       return;
2138   } else
2139     t_limit = state->chptr->mode.limit;
2140
2141   /* If they're not an oper, they can't change modes */
2142   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2143     send_notoper(state);
2144     return;
2145   }
2146
2147   if (state->done & DONE_LIMIT) /* allow limit to be set only once */
2148     return;
2149   state->done |= DONE_LIMIT;
2150
2151   if (!state->mbuf)
2152     return;
2153
2154   modebuf_mode_uint(state->mbuf, state->dir | flag_p[0], t_limit);
2155
2156   if (state->flags & MODE_PARSE_SET) { /* set the limit */
2157     if (state->dir & MODE_ADD) {
2158       state->chptr->mode.mode |= flag_p[0];
2159       state->chptr->mode.limit = t_limit;
2160     } else {
2161       state->chptr->mode.mode &= ~flag_p[0];
2162       state->chptr->mode.limit = 0;
2163     }
2164   }
2165 }
2166
2167 /*
2168  * Helper function to convert keys
2169  */
2170 static void
2171 mode_parse_key(struct ParseState *state, int *flag_p)
2172 {
2173   char *t_str, *s;
2174   int t_len;
2175
2176   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2177     return;
2178
2179   if (state->parc <= 0) { /* warn if not enough args */
2180     if (MyUser(state->sptr))
2181       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +k" :
2182                        "MODE -k");
2183     return;
2184   }
2185
2186   t_str = state->parv[state->args_used++]; /* grab arg */
2187   state->parc--;
2188   state->max_args--;
2189
2190   /* If they're not an oper, they can't change modes */
2191   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2192     send_notoper(state);
2193     return;
2194   }
2195
2196   if (state->done & DONE_KEY) /* allow key to be set only once */
2197     return;
2198   state->done |= DONE_KEY;
2199
2200   t_len = KEYLEN;
2201
2202   /* clean up the key string */
2203   s = t_str;
2204   while (*++s > ' ' && *s != ':' && --t_len)
2205     ;
2206   *s = '\0';
2207
2208   if (!*t_str) { /* warn if empty */
2209     if (MyUser(state->sptr))
2210       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +k" :
2211                        "MODE -k");
2212     return;
2213   }
2214
2215   if (!state->mbuf)
2216     return;
2217
2218   /* can't add a key if one is set, nor can one remove the wrong key */
2219   if (!(state->flags & MODE_PARSE_FORCE))
2220     if ((state->dir == MODE_ADD && *state->chptr->mode.key) ||
2221         (state->dir == MODE_DEL &&
2222          ircd_strcmp(state->chptr->mode.key, t_str))) {
2223       send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
2224       return;
2225     }
2226
2227   if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
2228       !ircd_strcmp(state->chptr->mode.key, t_str))
2229     return; /* no key change */
2230
2231   if (state->flags & MODE_PARSE_BOUNCE) {
2232     if (*state->chptr->mode.key) /* reset old key */
2233       modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
2234                           state->chptr->mode.key, 0);
2235     else /* remove new bogus key */
2236       modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
2237   } else /* send new key */
2238     modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
2239
2240   if (state->flags & MODE_PARSE_SET) {
2241     if (state->dir == MODE_ADD) /* set the new key */
2242       ircd_strncpy(state->chptr->mode.key, t_str, KEYLEN);
2243     else /* remove the old key */
2244       *state->chptr->mode.key = '\0';
2245   }
2246 }
2247
2248 /*
2249  * Helper function to convert user passes
2250  */
2251 static void
2252 mode_parse_upass(struct ParseState *state, int *flag_p)
2253 {
2254   char *t_str, *s;
2255   int t_len;
2256
2257   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2258     return;
2259
2260   if (state->parc <= 0) { /* warn if not enough args */
2261     if (MyUser(state->sptr))
2262       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +u" :
2263                        "MODE -u");
2264     return;
2265   }
2266
2267   t_str = state->parv[state->args_used++]; /* grab arg */
2268   state->parc--;
2269   state->max_args--;
2270
2271   /* If they're not an oper, they can't change modes */
2272   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2273     send_notoper(state);
2274     return;
2275   }
2276
2277   /* If they are not the channel manager, they are not allowed to change it */
2278   if (MyUser(state->sptr) && !IsChannelManager(state->member)) {
2279     if (*state->chptr->mode.apass) {
2280       send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
2281           "Use /JOIN", state->chptr->chname, "<AdminPass>.");
2282     } else {
2283       send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
2284           "Re-create the channel.  The channel must be *empty* for",
2285           TStime() - state->chptr->creationtime >= 171000 ? "48 contiguous hours" : "a minute or two",
2286           "before it can be recreated.");
2287     }
2288     return;
2289   }
2290  
2291   if (state->done & DONE_UPASS) /* allow upass to be set only once */
2292     return;
2293   state->done |= DONE_UPASS;
2294
2295   t_len = PASSLEN + 1;
2296
2297   /* clean up the upass string */
2298   s = t_str;
2299   while (*++s > ' ' && *s != ':' && --t_len)
2300     ;
2301   *s = '\0';
2302
2303   if (!*t_str) { /* warn if empty */
2304     if (MyUser(state->sptr))
2305       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +u" :
2306                        "MODE -u");
2307     return;
2308   }
2309
2310   if (!state->mbuf)
2311     return;
2312
2313   if (!(state->flags & MODE_PARSE_FORCE))
2314     /* can't add the upass while apass is not set */
2315     if (state->dir == MODE_ADD && !*state->chptr->mode.apass) {
2316       send_reply(state->sptr, ERR_UPASSNOTSET, state->chptr->chname, state->chptr->chname);
2317       return;
2318     }
2319     /* can't add a upass if one is set, nor can one remove the wrong upass */
2320     if ((state->dir == MODE_ADD && *state->chptr->mode.upass) ||
2321         (state->dir == MODE_DEL &&
2322          ircd_strcmp(state->chptr->mode.upass, t_str))) {
2323       send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
2324       return;
2325     }
2326
2327   if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
2328       !ircd_strcmp(state->chptr->mode.upass, t_str))
2329     return; /* no upass change */
2330
2331   if (state->flags & MODE_PARSE_BOUNCE) {
2332     if (*state->chptr->mode.upass) /* reset old upass */
2333       modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
2334                           state->chptr->mode.upass, 0);
2335     else /* remove new bogus upass */
2336       modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
2337   } else /* send new upass */
2338     modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
2339
2340   if (state->flags & MODE_PARSE_SET) {
2341     if (state->dir == MODE_ADD) /* set the new upass */
2342       ircd_strncpy(state->chptr->mode.upass, t_str, PASSLEN);
2343     else /* remove the old upass */
2344       *state->chptr->mode.upass = '\0';
2345   }
2346 }
2347
2348 /*
2349  * Helper function to convert admin passes
2350  */
2351 static void
2352 mode_parse_apass(struct ParseState *state, int *flag_p)
2353 {
2354   char *t_str, *s;
2355   int t_len;
2356
2357   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2358     return;
2359
2360   if (state->parc <= 0) { /* warn if not enough args */
2361     if (MyUser(state->sptr))
2362       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +A" :
2363                        "MODE -A");
2364     return;
2365   }
2366
2367   t_str = state->parv[state->args_used++]; /* grab arg */
2368   state->parc--;
2369   state->max_args--;
2370
2371   /* If they're not an oper, they can't change modes */
2372   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2373     send_notoper(state);
2374     return;
2375   }
2376
2377   /* Don't allow to change the Apass if the channel is older than 48 hours. */
2378   if (TStime() - state->chptr->creationtime >= 172800 && !IsAnOper(state->sptr)) {
2379     send_reply(state->sptr, ERR_CHANSECURED, state->chptr->chname);
2380     return;
2381   }
2382
2383   /* If they are not the channel manager, they are not allowed to change it */
2384   if (MyUser(state->sptr) && !IsChannelManager(state->member)) {
2385     if (*state->chptr->mode.apass) {
2386       send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
2387           "Use /JOIN", state->chptr->chname, "<AdminPass>.");
2388     } else {
2389       send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
2390           "Re-create the channel.  The channel must be *empty* for",
2391           "at least a whole minute", "before it can be recreated.");
2392     }
2393     return;
2394   }
2395  
2396   if (state->done & DONE_APASS) /* allow apass to be set only once */
2397     return;
2398   state->done |= DONE_APASS;
2399
2400   t_len = PASSLEN + 1;
2401
2402   /* clean up the apass string */
2403   s = t_str;
2404   while (*++s > ' ' && *s != ':' && --t_len)
2405     ;
2406   *s = '\0';
2407
2408   if (!*t_str) { /* warn if empty */
2409     if (MyUser(state->sptr))
2410       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +A" :
2411                        "MODE -A");
2412     return;
2413   }
2414
2415   if (!state->mbuf)
2416     return;
2417
2418   if (!(state->flags & MODE_PARSE_FORCE)) {
2419     /* can't remove the apass while upass is still set */
2420     if (state->dir == MODE_DEL && *state->chptr->mode.upass) {
2421       send_reply(state->sptr, ERR_UPASSSET, state->chptr->chname, state->chptr->chname);
2422       return;
2423     }
2424     /* can't add an apass if one is set, nor can one remove the wrong apass */
2425     if ((state->dir == MODE_ADD && *state->chptr->mode.apass) ||
2426         (state->dir == MODE_DEL && ircd_strcmp(state->chptr->mode.apass, t_str))) {
2427       send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
2428       return;
2429     }
2430   }
2431
2432   if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
2433       !ircd_strcmp(state->chptr->mode.apass, t_str))
2434     return; /* no apass change */
2435
2436   if (state->flags & MODE_PARSE_BOUNCE) {
2437     if (*state->chptr->mode.apass) /* reset old apass */
2438       modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
2439                           state->chptr->mode.apass, 0);
2440     else /* remove new bogus apass */
2441       modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
2442   } else /* send new apass */
2443     modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
2444
2445   if (state->flags & MODE_PARSE_SET) {
2446     if (state->dir == MODE_ADD) { /* set the new apass */
2447       /* Make it VERY clear to the user that this is a one-time password */
2448       ircd_strncpy(state->chptr->mode.apass, t_str, PASSLEN);
2449       if (MyUser(state->sptr)) {
2450         send_reply(state->sptr, RPL_APASSWARN,
2451             "Channel Admin password (+A) set to '", state->chptr->mode.apass, "'. ",
2452             "Are you SURE you want to use this as Admin password? ",
2453             "You will NOT be able to change this password anymore once the channel is more than 48 hours old!");
2454         send_reply(state->sptr, RPL_APASSWARN,
2455             "Use \"/MODE ", state->chptr->chname, " -A ", state->chptr->mode.apass,
2456             "\" to remove the password and then immediately set a new one. "
2457             "IMPORTANT: YOU CANNOT RECOVER THIS PASSWORD, EVER; "
2458             "WRITE THE PASSWORD DOWN (don't store this rescue password on disk)! "
2459             "Now set the channel user password (+u).");
2460       }
2461     } else { /* remove the old apass */
2462       *state->chptr->mode.apass = '\0';
2463       if (MyUser(state->sptr))
2464         send_reply(state->sptr, RPL_APASSWARN,
2465             "WARNING: You removed the channel Admin password MODE (+A). ",
2466             "If you would disconnect or leave the channel without setting a new password then you will ",
2467             "not be able to set it again and lose ownership of this channel! ",
2468             "SET A NEW PASSWORD NOW!", "");
2469     }
2470   }
2471 }
2472
2473 /*
2474  * Helper function to convert bans
2475  */
2476 static void
2477 mode_parse_ban(struct ParseState *state, int *flag_p)
2478 {
2479   char *t_str, *s;
2480   struct SLink *ban, *newban = 0;
2481
2482   if (state->parc <= 0) { /* Not enough args, send ban list */
2483     if (MyUser(state->sptr) && !(state->done & DONE_BANLIST)) {
2484       send_ban_list(state->sptr, state->chptr);
2485       state->done |= DONE_BANLIST;
2486     }
2487
2488     return;
2489   }
2490
2491   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2492     return;
2493
2494   t_str = state->parv[state->args_used++]; /* grab arg */
2495   state->parc--;
2496   state->max_args--;
2497
2498   /* If they're not an oper, they can't change modes */
2499   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2500     send_notoper(state);
2501     return;
2502   }
2503
2504   if ((s = strchr(t_str, ' ')))
2505     *s = '\0';
2506
2507   if (!*t_str || *t_str == ':') { /* warn if empty */
2508     if (MyUser(state->sptr))
2509       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +b" :
2510                        "MODE -b");
2511     return;
2512   }
2513
2514   t_str = collapse(pretty_mask(t_str));
2515
2516   /* remember the ban for the moment... */
2517   if (state->dir == MODE_ADD) {
2518     newban = state->banlist + (state->numbans++);
2519     newban->next = 0;
2520
2521     DupString(newban->value.ban.banstr, t_str);
2522     newban->value.ban.who = cli_name(state->sptr);
2523     newban->value.ban.when = TStime();
2524
2525     newban->flags = CHFL_BAN | MODE_ADD;
2526
2527     if ((s = strrchr(t_str, '@')) && check_if_ipmask(s + 1))
2528       newban->flags |= CHFL_BAN_IPMASK;
2529   }
2530
2531   if (!state->chptr->banlist) {
2532     state->chptr->banlist = newban; /* add our ban with its flags */
2533     state->done |= DONE_BANCLEAN;
2534     return;
2535   }
2536
2537   /* Go through all bans */
2538   for (ban = state->chptr->banlist; ban; ban = ban->next) {
2539     /* first, clean the ban flags up a bit */
2540     if (!(state->done & DONE_BANCLEAN))
2541       /* Note: We're overloading *lots* of bits here; be careful! */
2542       ban->flags &= ~(MODE_ADD | MODE_DEL | CHFL_BAN_OVERLAPPED);
2543
2544     /* Bit meanings:
2545      *
2546      * MODE_ADD            - Ban was added; if we're bouncing modes,
2547      *                       then we'll remove it below; otherwise,
2548      *                       we'll have to allocate a real ban
2549      *
2550      * MODE_DEL            - Ban was marked for deletion; if we're
2551      *                       bouncing modes, we'll have to re-add it,
2552      *                       otherwise, we'll have to remove it
2553      *
2554      * CHFL_BAN_OVERLAPPED - The ban we added turns out to overlap
2555      *                       with a ban already set; if we're
2556      *                       bouncing modes, we'll have to bounce
2557      *                       this one; otherwise, we'll just ignore
2558      *                       it when we process added bans
2559      */
2560
2561     if (state->dir == MODE_DEL && !ircd_strcmp(ban->value.ban.banstr, t_str)) {
2562       ban->flags |= MODE_DEL; /* delete one ban */
2563
2564       if (state->done & DONE_BANCLEAN) /* If we're cleaning, finish */
2565         break;
2566     } else if (state->dir == MODE_ADD) {
2567       /* if the ban already exists, don't worry about it */
2568       if (!ircd_strcmp(ban->value.ban.banstr, t_str)) {
2569         newban->flags &= ~MODE_ADD; /* don't add ban at all */
2570         MyFree(newban->value.ban.banstr); /* stopper a leak */
2571         state->numbans--; /* deallocate last ban */
2572         if (state->done & DONE_BANCLEAN) /* If we're cleaning, finish */
2573           break;
2574       } else if (!mmatch(ban->value.ban.banstr, t_str)) {
2575         if (!(ban->flags & MODE_DEL))
2576           newban->flags |= CHFL_BAN_OVERLAPPED; /* our ban overlaps */
2577       } else if (!mmatch(t_str, ban->value.ban.banstr))
2578         ban->flags |= MODE_DEL; /* mark ban for deletion: overlapping */
2579
2580       if (!ban->next && (newban->flags & MODE_ADD))
2581       {
2582         ban->next = newban; /* add our ban with its flags */
2583         break; /* get out of loop */
2584       }
2585     }
2586   }
2587   state->done |= DONE_BANCLEAN;
2588 }
2589
2590 /*
2591  * This is the bottom half of the ban processor
2592  */
2593 static void
2594 mode_process_bans(struct ParseState *state)
2595 {
2596   struct SLink *ban, *newban, *prevban, *nextban;
2597   int count = 0;
2598   int len = 0;
2599   int banlen;
2600   int changed = 0;
2601
2602   for (prevban = 0, ban = state->chptr->banlist; ban; ban = nextban) {
2603     count++;
2604     banlen = strlen(ban->value.ban.banstr);
2605     len += banlen;
2606     nextban = ban->next;
2607
2608     if ((ban->flags & (MODE_DEL | MODE_ADD)) == (MODE_DEL | MODE_ADD)) {
2609       if (prevban)
2610         prevban->next = 0; /* Break the list; ban isn't a real ban */
2611       else
2612         state->chptr->banlist = 0;
2613
2614       count--;
2615       len -= banlen;
2616
2617       MyFree(ban->value.ban.banstr);
2618
2619       continue;
2620     } else if (ban->flags & MODE_DEL) { /* Deleted a ban? */
2621       modebuf_mode_string(state->mbuf, MODE_DEL | MODE_BAN,
2622                           ban->value.ban.banstr,
2623                           state->flags & MODE_PARSE_SET);
2624
2625       if (state->flags & MODE_PARSE_SET) { /* Ok, make it take effect */
2626         if (prevban) /* clip it out of the list... */
2627           prevban->next = ban->next;
2628         else
2629           state->chptr->banlist = ban->next;
2630
2631         count--;
2632         len -= banlen;
2633
2634         MyFree(ban->value.ban.who);
2635         free_link(ban);
2636
2637         changed++;
2638         continue; /* next ban; keep prevban like it is */
2639       } else
2640         ban->flags &= (CHFL_BAN | CHFL_BAN_IPMASK); /* unset other flags */
2641     } else if (ban->flags & MODE_ADD) { /* adding a ban? */
2642       if (prevban)
2643         prevban->next = 0; /* Break the list; ban isn't a real ban */
2644       else
2645         state->chptr->banlist = 0;
2646
2647       /* If we're supposed to ignore it, do so. */
2648       if (ban->flags & CHFL_BAN_OVERLAPPED &&
2649           !(state->flags & MODE_PARSE_BOUNCE)) {
2650         count--;
2651         len -= banlen;
2652
2653         MyFree(ban->value.ban.banstr);
2654       } else {
2655         if (state->flags & MODE_PARSE_SET && MyUser(state->sptr) &&
2656             (len > (feature_int(FEAT_AVBANLEN) * feature_int(FEAT_MAXBANS)) ||
2657              count > feature_int(FEAT_MAXBANS))) {
2658           send_reply(state->sptr, ERR_BANLISTFULL, state->chptr->chname,
2659                      ban->value.ban.banstr);
2660           count--;
2661           len -= banlen;
2662
2663           MyFree(ban->value.ban.banstr);
2664         } else {
2665           /* add the ban to the buffer */
2666           modebuf_mode_string(state->mbuf, MODE_ADD | MODE_BAN,
2667                               ban->value.ban.banstr,
2668                               !(state->flags & MODE_PARSE_SET));
2669
2670           if (state->flags & MODE_PARSE_SET) { /* create a new ban */
2671             newban = make_link();
2672             newban->value.ban.banstr = ban->value.ban.banstr;
2673             DupString(newban->value.ban.who, ban->value.ban.who);
2674             newban->value.ban.when = ban->value.ban.when;
2675             newban->flags = ban->flags & (CHFL_BAN | CHFL_BAN_IPMASK);
2676
2677             newban->next = state->chptr->banlist; /* and link it in */
2678             state->chptr->banlist = newban;
2679
2680             changed++;
2681           }
2682         }
2683       }
2684     }
2685
2686     prevban = ban;
2687   } /* for (prevban = 0, ban = state->chptr->banlist; ban; ban = nextban) { */
2688
2689   if (changed) /* if we changed the ban list, we must invalidate the bans */
2690     mode_ban_invalidate(state->chptr);
2691 }
2692
2693 /*
2694  * Helper function to process client changes
2695  */
2696 static void
2697 mode_parse_client(struct ParseState *state, int *flag_p)
2698 {
2699   char *t_str;
2700   struct Client *acptr;
2701   int i;
2702
2703   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2704     return;
2705
2706   if (state->parc <= 0) /* return if not enough args */
2707     return;
2708
2709   t_str = state->parv[state->args_used++]; /* grab arg */
2710   state->parc--;
2711   state->max_args--;
2712
2713   /* If they're not an oper, they can't change modes */
2714   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2715     send_notoper(state);
2716     return;
2717   }
2718
2719   if (MyUser(state->sptr)) /* find client we're manipulating */
2720     acptr = find_chasing(state->sptr, t_str, NULL);
2721   else
2722     acptr = findNUser(t_str);
2723
2724   if (!acptr)
2725     return; /* find_chasing() already reported an error to the user */
2726
2727   for (i = 0; i < MAXPARA; i++) /* find an element to stick them in */
2728     if (!state->cli_change[i].flag || (state->cli_change[i].client == acptr &&
2729                                        state->cli_change[i].flag & flag_p[0]))
2730       break; /* found a slot */
2731
2732   /* Store what we're doing to them */
2733   state->cli_change[i].flag = state->dir | flag_p[0];
2734   state->cli_change[i].client = acptr;
2735 }
2736
2737 /*
2738  * Helper function to process the changed client list
2739  */
2740 static void
2741 mode_process_clients(struct ParseState *state)
2742 {
2743   int i;
2744   struct Membership *member;
2745
2746   for (i = 0; state->cli_change[i].flag; i++) {
2747     assert(0 != state->cli_change[i].client);
2748
2749     /* look up member link */
2750     if (!(member = find_member_link(state->chptr,
2751                                     state->cli_change[i].client)) ||
2752         (MyUser(state->sptr) && IsZombie(member))) {
2753       if (MyUser(state->sptr))
2754         send_reply(state->sptr, ERR_USERNOTINCHANNEL,
2755                    cli_name(state->cli_change[i].client),
2756                    state->chptr->chname);
2757       continue;
2758     }
2759
2760     if ((state->cli_change[i].flag & MODE_ADD &&
2761          (state->cli_change[i].flag & member->status)) ||
2762         (state->cli_change[i].flag & MODE_DEL &&
2763          !(state->cli_change[i].flag & member->status)))
2764       continue; /* no change made, don't do anything */
2765
2766     /* see if the deop is allowed */
2767     if ((state->cli_change[i].flag & (MODE_DEL | MODE_CHANOP)) ==
2768         (MODE_DEL | MODE_CHANOP)) {
2769       /* prevent +k users from being deopped */
2770       if (IsChannelService(state->cli_change[i].client)) {
2771         if (state->flags & MODE_PARSE_FORCE) /* it was forced */
2772           sendto_opmask_butone(0, SNO_HACK4, "Deop of +k user on %H by %s",
2773                                state->chptr,
2774                                (IsServer(state->sptr) ? cli_name(state->sptr) :
2775                                 cli_name((cli_user(state->sptr))->server)));
2776
2777         else if (MyUser(state->sptr) && state->flags & MODE_PARSE_SET) {
2778           send_reply(state->sptr, ERR_ISCHANSERVICE,
2779                      cli_name(state->cli_change[i].client),
2780                      state->chptr->chname);
2781           continue;
2782         }
2783       }
2784
2785       /* check deop for local user */
2786       if (MyUser(state->sptr)) {
2787
2788         /* don't allow local opers to be deopped on local channels */
2789         if (MyUser(state->sptr) &&
2790             state->cli_change[i].client != state->sptr &&
2791             IsLocalChannel(state->chptr->chname) &&
2792             HasPriv(state->cli_change[i].client, PRIV_DEOP_LCHAN)) {
2793           send_reply(state->sptr, ERR_ISOPERLCHAN,
2794                      cli_name(state->cli_change[i].client),
2795                      state->chptr->chname);
2796           continue;
2797         }
2798
2799         /* don't allow to deop members with an op level that is <= our own level */
2800         if (state->sptr != state->cli_change[i].client          /* but allow to deop oneself */
2801                 && state->member
2802                 && OpLevel(member) <= OpLevel(state->member)) {
2803             int equal = (OpLevel(member) == OpLevel(state->member));
2804             send_reply(state->sptr, ERR_NOTLOWEROPLEVEL,
2805                        cli_name(state->cli_change[i].client),
2806                        state->chptr->chname,
2807                        OpLevel(state->member), OpLevel(member),
2808                        "deop", equal ? "the same" : "a higher");
2809           continue;
2810         }
2811       }
2812     }
2813
2814     /* set op-level of member being opped */
2815     if ((state->cli_change[i].flag & (MODE_ADD | MODE_CHANOP)) ==
2816         (MODE_ADD | MODE_CHANOP)) {
2817       /* If on a channel with upass set, someone with level x gives ops to someone else,
2818          then that person gets level x-1.  On other channels, where upass is not set,
2819          the level stays the same. */
2820       int level_increment = *state->chptr->mode.upass ? 1 : 0;
2821       /* Someone being opped by a server gets op-level 0 */
2822       int old_level = (state->member == NULL) ? -level_increment : OpLevel(state->member);
2823       SetOpLevel(member, old_level == MAXOPLEVEL ? MAXOPLEVEL : (old_level + level_increment));
2824     }
2825
2826     /* accumulate the change */
2827     modebuf_mode_client(state->mbuf, state->cli_change[i].flag,
2828                         state->cli_change[i].client);
2829
2830     /* actually effect the change */
2831     if (state->flags & MODE_PARSE_SET) {
2832       if (state->cli_change[i].flag & MODE_ADD) {
2833         member->status |= (state->cli_change[i].flag &
2834                            (MODE_CHANOP | MODE_VOICE));
2835         if (state->cli_change[i].flag & MODE_CHANOP)
2836           ClearDeopped(member);
2837       } else
2838         member->status &= ~(state->cli_change[i].flag &
2839                             (MODE_CHANOP | MODE_VOICE));
2840     }
2841   } /* for (i = 0; state->cli_change[i].flags; i++) */
2842 }
2843
2844 /*
2845  * Helper function to process the simple modes
2846  */
2847 static void
2848 mode_parse_mode(struct ParseState *state, int *flag_p)
2849 {
2850   /* If they're not an oper, they can't change modes */
2851   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2852     send_notoper(state);
2853     return;
2854   }
2855
2856   if (!state->mbuf)
2857     return;
2858
2859   if (state->dir == MODE_ADD) {
2860     state->add |= flag_p[0];
2861     state->del &= ~flag_p[0];
2862
2863     if (flag_p[0] & MODE_SECRET) {
2864       state->add &= ~MODE_PRIVATE;
2865       state->del |= MODE_PRIVATE;
2866     } else if (flag_p[0] & MODE_PRIVATE) {
2867       state->add &= ~MODE_SECRET;
2868       state->del |= MODE_SECRET;
2869     }
2870   } else {
2871     state->add &= ~flag_p[0];
2872     state->del |= flag_p[0];
2873   }
2874
2875   assert(0 == (state->add & state->del));
2876   assert((MODE_SECRET | MODE_PRIVATE) !=
2877          (state->add & (MODE_SECRET | MODE_PRIVATE)));
2878 }
2879
2880 /*
2881  * This routine is intended to parse MODE or OPMODE commands and effect the
2882  * changes (or just build the bounce buffer).  We pass the starting offset
2883  * as a 
2884  */
2885 int
2886 mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
2887            struct Channel *chptr, int parc, char *parv[], unsigned int flags,
2888            struct Membership* member)
2889 {
2890   static int chan_flags[] = {
2891     MODE_CHANOP,        'o',
2892     MODE_VOICE,         'v',
2893     MODE_PRIVATE,       'p',
2894     MODE_SECRET,        's',
2895     MODE_MODERATED,     'm',
2896     MODE_TOPICLIMIT,    't',
2897     MODE_INVITEONLY,    'i',
2898     MODE_NOPRIVMSGS,    'n',
2899     MODE_KEY,           'k',
2900     MODE_APASS,         'A',
2901     MODE_UPASS,         'u',
2902     MODE_BAN,           'b',
2903     MODE_LIMIT,         'l',
2904     MODE_REGONLY,       'r',
2905     MODE_ADD,           '+',
2906     MODE_DEL,           '-',
2907     0x0, 0x0
2908   };
2909   int i;
2910   int *flag_p;
2911   unsigned int t_mode;
2912   char *modestr;
2913   struct ParseState state;
2914
2915   assert(0 != cptr);
2916   assert(0 != sptr);
2917   assert(0 != chptr);
2918   assert(0 != parc);
2919   assert(0 != parv);
2920
2921   state.mbuf = mbuf;
2922   state.cptr = cptr;
2923   state.sptr = sptr;
2924   state.chptr = chptr;
2925   state.member = member;
2926   state.parc = parc;
2927   state.parv = parv;
2928   state.flags = flags;
2929   state.dir = MODE_ADD;
2930   state.done = 0;
2931   state.add = 0;
2932   state.del = 0;
2933   state.args_used = 0;
2934   state.max_args = MAXMODEPARAMS;
2935   state.numbans = 0;
2936
2937   for (i = 0; i < MAXPARA; i++) { /* initialize ops/voices arrays */
2938     state.banlist[i].next = 0;
2939     state.banlist[i].value.ban.banstr = 0;
2940     state.banlist[i].value.ban.who = 0;
2941     state.banlist[i].value.ban.when = 0;
2942     state.banlist[i].flags = 0;
2943     state.cli_change[i].flag = 0;
2944     state.cli_change[i].client = 0;
2945   }
2946
2947   modestr = state.parv[state.args_used++];
2948   state.parc--;
2949
2950   while (*modestr) {
2951     for (; *modestr; modestr++) {
2952       for (flag_p = chan_flags; flag_p[0]; flag_p += 2) /* look up flag */
2953         if (flag_p[1] == *modestr)
2954           break;
2955
2956       if (!flag_p[0]) { /* didn't find it?  complain and continue */
2957         if (MyUser(state.sptr))
2958           send_reply(state.sptr, ERR_UNKNOWNMODE, *modestr);
2959         continue;
2960       }
2961
2962       switch (*modestr) {
2963       case '+': /* switch direction to MODE_ADD */
2964       case '-': /* switch direction to MODE_DEL */
2965         state.dir = flag_p[0];
2966         break;
2967
2968       case 'l': /* deal with limits */
2969         mode_parse_limit(&state, flag_p);
2970         break;
2971
2972       case 'k': /* deal with keys */
2973         mode_parse_key(&state, flag_p);
2974         break;
2975
2976       case 'A': /* deal with Admin passes */
2977         mode_parse_apass(&state, flag_p);
2978         break;
2979
2980       case 'u': /* deal with user passes */
2981         mode_parse_upass(&state, flag_p);
2982         break;
2983
2984       case 'b': /* deal with bans */
2985         mode_parse_ban(&state, flag_p);
2986         break;
2987
2988       case 'o': /* deal with ops/voice */
2989       case 'v':
2990         mode_parse_client(&state, flag_p);
2991         break;
2992
2993       default: /* deal with other modes */
2994         mode_parse_mode(&state, flag_p);
2995         break;
2996       } /* switch (*modestr) */
2997     } /* for (; *modestr; modestr++) */
2998
2999     if (state.flags & MODE_PARSE_BURST)
3000       break; /* don't interpret any more arguments */
3001
3002     if (state.parc > 0) { /* process next argument in string */
3003       modestr = state.parv[state.args_used++];
3004       state.parc--;
3005
3006       /* is it a TS? */
3007       if (IsServer(state.sptr) && !state.parc && IsDigit(*modestr)) {
3008         time_t recv_ts;
3009
3010         if (!(state.flags & MODE_PARSE_SET))      /* don't set earlier TS if */
3011           break;                     /* we're then going to bounce the mode! */
3012
3013         recv_ts = atoi(modestr);
3014
3015         if (recv_ts && recv_ts < state.chptr->creationtime)
3016           state.chptr->creationtime = recv_ts; /* respect earlier TS */
3017
3018         break; /* break out of while loop */
3019       } else if (state.flags & MODE_PARSE_STRICT ||
3020                  (MyUser(state.sptr) && state.max_args <= 0)) {
3021         state.parc++; /* we didn't actually gobble the argument */
3022         state.args_used--;
3023         break; /* break out of while loop */
3024       }
3025     }
3026   } /* while (*modestr) */
3027
3028   /*
3029    * the rest of the function finishes building resultant MODEs; if the
3030    * origin isn't a member or an oper, skip it.
3031    */
3032   if (!state.mbuf || state.flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER))
3033     return state.args_used; /* tell our parent how many args we gobbled */
3034
3035   t_mode = state.chptr->mode.mode;
3036
3037   if (state.del & t_mode) { /* delete any modes to be deleted... */
3038     modebuf_mode(state.mbuf, MODE_DEL | (state.del & t_mode));
3039
3040     t_mode &= ~state.del;
3041   }
3042   if (state.add & ~t_mode) { /* add any modes to be added... */
3043     modebuf_mode(state.mbuf, MODE_ADD | (state.add & ~t_mode));
3044
3045     t_mode |= state.add;
3046   }
3047
3048   if (state.flags & MODE_PARSE_SET) { /* set the channel modes */
3049     if ((state.chptr->mode.mode & MODE_INVITEONLY) &&
3050         !(t_mode & MODE_INVITEONLY))
3051       mode_invite_clear(state.chptr);
3052
3053     state.chptr->mode.mode = t_mode;
3054   }
3055
3056   if (state.flags & MODE_PARSE_WIPEOUT) {
3057     if (state.chptr->mode.limit && !(state.done & DONE_LIMIT))
3058       modebuf_mode_uint(state.mbuf, MODE_DEL | MODE_LIMIT,
3059                         state.chptr->mode.limit);
3060     if (*state.chptr->mode.key && !(state.done & DONE_KEY))
3061       modebuf_mode_string(state.mbuf, MODE_DEL | MODE_KEY,
3062                           state.chptr->mode.key, 0);
3063     if (*state.chptr->mode.upass && !(state.done & DONE_UPASS))
3064       modebuf_mode_string(state.mbuf, MODE_DEL | MODE_UPASS,
3065                           state.chptr->mode.upass, 0);
3066     if (*state.chptr->mode.apass && !(state.done & DONE_APASS))
3067       modebuf_mode_string(state.mbuf, MODE_DEL | MODE_APASS,
3068                           state.chptr->mode.apass, 0);
3069   }
3070
3071   if (state.done & DONE_BANCLEAN) /* process bans */
3072     mode_process_bans(&state);
3073
3074   /* process client changes */
3075   if (state.cli_change[0].flag)
3076     mode_process_clients(&state);
3077
3078   return state.args_used; /* tell our parent how many args we gobbled */
3079 }
3080
3081 /*
3082  * Initialize a join buffer
3083  */
3084 void
3085 joinbuf_init(struct JoinBuf *jbuf, struct Client *source,
3086              struct Client *connect, unsigned int type, char *comment,
3087              time_t create)
3088 {
3089   int i;
3090
3091   assert(0 != jbuf);
3092   assert(0 != source);
3093   assert(0 != connect);
3094
3095   jbuf->jb_source = source; /* just initialize struct JoinBuf */
3096   jbuf->jb_connect = connect;
3097   jbuf->jb_type = type;
3098   jbuf->jb_comment = comment;
3099   jbuf->jb_create = create;
3100   jbuf->jb_count = 0;
3101   jbuf->jb_strlen = (((type == JOINBUF_TYPE_JOIN ||
3102                        type == JOINBUF_TYPE_PART ||
3103                        type == JOINBUF_TYPE_PARTALL) ?
3104                       STARTJOINLEN : STARTCREATELEN) +
3105                      (comment ? strlen(comment) + 2 : 0));
3106
3107   for (i = 0; i < MAXJOINARGS; i++)
3108     jbuf->jb_channels[i] = 0;
3109 }
3110
3111 /*
3112  * Add a channel to the join buffer
3113  */
3114 void
3115 joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
3116 {
3117   unsigned int len;
3118   int is_local;
3119
3120   assert(0 != jbuf);
3121
3122   if (!chan) {
3123     if (jbuf->jb_type == JOINBUF_TYPE_JOIN)
3124       sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect, "0");
3125
3126     return;
3127   }
3128
3129   is_local = IsLocalChannel(chan->chname);
3130
3131   if (jbuf->jb_type == JOINBUF_TYPE_PART ||
3132       jbuf->jb_type == JOINBUF_TYPE_PARTALL) {
3133     /* Send notification to channel */
3134     if (!(flags & CHFL_ZOMBIE))
3135       sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_PART, chan, NULL,
3136                                 (flags & CHFL_BANNED || !jbuf->jb_comment) ?
3137                                 ":%H" : "%H :%s", chan, jbuf->jb_comment);
3138     else if (MyUser(jbuf->jb_source))
3139       sendcmdto_one(jbuf->jb_source, CMD_PART, jbuf->jb_source,
3140                     (flags & CHFL_BANNED || !jbuf->jb_comment) ?
3141                     ":%H" : "%H :%s", chan, jbuf->jb_comment);
3142     /* XXX: Shouldn't we send a PART here anyway? */
3143     /* to users on the channel?  Why?  From their POV, the user isn't on
3144      * the channel anymore anyway.  We don't send to servers until below,
3145      * when we gang all the channel parts together.  Note that this is
3146      * exactly the same logic, albeit somewhat more concise, as was in
3147      * the original m_part.c */
3148
3149     if (jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
3150         is_local) /* got to remove user here */
3151       remove_user_from_channel(jbuf->jb_source, chan);
3152   } else {
3153     /* Add user to channel */
3154     add_user_to_channel(chan, jbuf->jb_source, flags, 0);
3155
3156     /* send notification to all servers */
3157     if (jbuf->jb_type != JOINBUF_TYPE_CREATE && !is_local)
3158       sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect,
3159                             "%H %Tu", chan, chan->creationtime);
3160
3161     /* Send the notification to the channel */
3162     sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_JOIN, chan, NULL, ":%H", chan);
3163
3164     /* send an op, too, if needed */
3165     if (!MyUser(jbuf->jb_source) && jbuf->jb_type == JOINBUF_TYPE_CREATE)
3166       sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_MODE, chan, NULL, "%H +o %C",
3167                                 chan, jbuf->jb_source);
3168   }
3169
3170   if (jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
3171       jbuf->jb_type == JOINBUF_TYPE_JOIN || is_local)
3172     return; /* don't send to remote */
3173
3174   /* figure out if channel name will cause buffer to be overflowed */
3175   len = chan ? strlen(chan->chname) + 1 : 2;
3176   if (jbuf->jb_strlen + len > BUFSIZE)
3177     joinbuf_flush(jbuf);
3178
3179   /* add channel to list of channels to send and update counts */
3180   jbuf->jb_channels[jbuf->jb_count++] = chan;
3181   jbuf->jb_strlen += len;
3182
3183   /* if we've used up all slots, flush */
3184   if (jbuf->jb_count >= MAXJOINARGS)
3185     joinbuf_flush(jbuf);
3186 }
3187
3188 /*
3189  * Flush the channel list to remote servers
3190  */
3191 int
3192 joinbuf_flush(struct JoinBuf *jbuf)
3193 {
3194   char chanlist[BUFSIZE];
3195   int chanlist_i = 0;
3196   int i;
3197
3198   if (!jbuf->jb_count || jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
3199       jbuf->jb_type == JOINBUF_TYPE_JOIN)
3200     return 0; /* no joins to process */
3201
3202   for (i = 0; i < jbuf->jb_count; i++) { /* build channel list */
3203     build_string(chanlist, &chanlist_i,
3204                  jbuf->jb_channels[i] ? jbuf->jb_channels[i]->chname : "0", 0,
3205                  i == 0 ? '\0' : ',');
3206     if (JOINBUF_TYPE_PART == jbuf->jb_type)
3207       /* Remove user from channel */
3208       remove_user_from_channel(jbuf->jb_source, jbuf->jb_channels[i]);
3209
3210     jbuf->jb_channels[i] = 0; /* mark slot empty */
3211   }
3212
3213   jbuf->jb_count = 0; /* reset base counters */
3214   jbuf->jb_strlen = ((jbuf->jb_type == JOINBUF_TYPE_PART ?
3215                       STARTJOINLEN : STARTCREATELEN) +
3216                      (jbuf->jb_comment ? strlen(jbuf->jb_comment) + 2 : 0));
3217
3218   /* and send the appropriate command */
3219   switch (jbuf->jb_type) {
3220   case JOINBUF_TYPE_CREATE:
3221     sendcmdto_serv_butone(jbuf->jb_source, CMD_CREATE, jbuf->jb_connect,
3222                           "%s %Tu", chanlist, jbuf->jb_create);
3223     break;
3224
3225   case JOINBUF_TYPE_PART:
3226     sendcmdto_serv_butone(jbuf->jb_source, CMD_PART, jbuf->jb_connect,
3227                           jbuf->jb_comment ? "%s :%s" : "%s", chanlist,
3228                           jbuf->jb_comment);
3229     break;
3230   }
3231
3232   return 0;
3233 }
3234
3235 /* Returns TRUE (1) if client is invited, FALSE (0) if not */
3236 int IsInvited(struct Client* cptr, const void* chptr)
3237 {
3238   struct SLink *lp;
3239
3240   for (lp = (cli_user(cptr))->invited; lp; lp = lp->next)
3241     if (lp->value.chptr == chptr)
3242       return 1;
3243   return 0;
3244 }