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