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