Fix oplevel handling in bursts, fixing SF bugs #2596869 and #2597518.
[ircu2.10.12-pk.git] / ircd / m_burst.c
1 /*
2  * IRC - Internet Relay Chat, ircd/m_burst.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  *
6  * See file AUTHORS in IRC package for additional names of
7  * the programmers.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 1, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * $Id$
24  */
25
26 /*
27  * m_functions execute protocol messages on this server:
28  *
29  *    cptr    is always NON-NULL, pointing to a *LOCAL* client
30  *            structure (with an open socket connected!). This
31  *            identifies the physical socket where the message
32  *            originated (or which caused the m_function to be
33  *            executed--some m_functions may call others...).
34  *
35  *    sptr    is the source of the message, defined by the
36  *            prefix part of the message if present. If not
37  *            or prefix not found, then sptr==cptr.
38  *
39  *            (!IsServer(cptr)) => (cptr == sptr), because
40  *            prefixes are taken *only* from servers...
41  *
42  *            (IsServer(cptr))
43  *                    (sptr == cptr) => the message didn't
44  *                    have the prefix.
45  *
46  *                    (sptr != cptr && IsServer(sptr) means
47  *                    the prefix specified servername. (?)
48  *
49  *                    (sptr != cptr && !IsServer(sptr) means
50  *                    that message originated from a remote
51  *                    user (not local).
52  *
53  *            combining
54  *
55  *            (!IsServer(sptr)) means that, sptr can safely
56  *            taken as defining the target structure of the
57  *            message in this server.
58  *
59  *    *Always* true (if 'parse' and others are working correct):
60  *
61  *    1)      sptr->from == cptr  (note: cptr->from == cptr)
62  *
63  *    2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr
64  *            *cannot* be a local connection, unless it's
65  *            actually cptr!). [MyConnect(x) should probably
66  *            be defined as (x == x->from) --msa ]
67  *
68  *    parc    number of variable parameter strings (if zero,
69  *            parv is allowed to be NULL)
70  *
71  *    parv    a NULL terminated list of parameter pointers,
72  *
73  *                    parv[0], sender (prefix string), if not present
74  *                            this points to an empty string.
75  *                    parv[1]...parv[parc-1]
76  *                            pointers to additional parameters
77  *                    parv[parc] == NULL, *always*
78  *
79  *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
80  *                    non-NULL pointers.
81  */
82 #include "config.h"
83
84 #include "channel.h"
85 #include "client.h"
86 #include "hash.h"
87 #include "ircd.h"
88 #include "ircd_alloc.h"
89 #include "ircd_features.h"
90 #include "ircd_log.h"
91 #include "ircd_reply.h"
92 #include "ircd_string.h"
93 #include "list.h"
94 #include "match.h"
95 #include "msg.h"
96 #include "numeric.h"
97 #include "numnicks.h"
98 #include "s_conf.h"
99 #include "s_misc.h"
100 #include "send.h"
101 #include "struct.h"
102 #include "ircd_snprintf.h"
103
104 /* #include <assert.h> -- Now using assert in ircd_log.h */
105 #include <stdlib.h>
106 #include <string.h>
107 #include <ctype.h>
108
109 static int
110 netride_modes(int parc, char **parv, const char *curr_key)
111 {
112   char *modes = parv[0];
113   int result = 0;
114
115   assert(modes && modes[0] == '+');
116   while (*modes) {
117     switch (*modes++) {
118     case '-':
119       return -1;
120     case 'i':
121       result |= MODE_INVITEONLY;
122       break;
123     case 'k':
124       if (strcmp(curr_key, *++parv))
125         result |= MODE_KEY;
126       break;
127     case 'l':
128       ++parv;
129       break;
130     case 'r':
131       result |= MODE_REGONLY;
132       break;
133     }
134   }
135   return result;
136 }
137
138 /*
139  * ms_burst - server message handler
140  *
141  * --  by Run carlo@runaway.xs4all.nl  december 1995 till march 1997
142  *
143  * parv[0] = sender prefix
144  * parv[1] = channel name
145  * parv[2] = channel timestamp
146  * The meaning of the following parv[]'s depend on their first character:
147  * If parv[n] starts with a '+':
148  * Net burst, additive modes
149  *   parv[n] = <mode>
150  *   parv[n+1] = <param> (optional)
151  *   parv[n+2] = <param> (optional)
152  * If parv[n] starts with a '%', then n will be parc-1:
153  *   parv[n] = %<ban> <ban> <ban> ...
154  * If parv[n] starts with another character:
155  *   parv[n] = <nick>[:<mode>],<nick>[:<mode>],...
156  *   where <mode> defines the mode and op-level
157  *   for nick and all following nicks until the
158  *   next <mode> field.
159  *   Digits in the <mode> field have of two meanings:
160  *   1) if it is the first field in this BURST message
161  *      that contains digits, and/or when a 'v' is
162  *      present in the <mode>:
163  *      The absolute value of the op-level.
164  *   2) if there are only digits in this field and
165  *      it is not the first field with digits:
166  *      An op-level increment relative to the previous
167  *      op-level.
168  *   First all modeless nicks must be emmitted,
169  *   then all combinations of modes without ops
170  *   (currently that is only 'v') followed by the same
171  *   series but then with ops (currently 'o','ov').
172  *
173  * Example:
174  * "A8 B #test 87654321 +ntkAl key secret 123 A8AAG,A8AAC:v,A8AAA:0,A8AAF:2,A8AAD,A8AAB:v1,A8AAE:1 :%ban1 ban2"
175  *
176  * <mode> list example:
177  *
178  * "xxx,sss:v,ttt,aaa:123,bbb,ccc:2,ddd,kkk:v2,lll:2,mmm"
179  *
180  * means
181  *
182  *  xxx         // first modeless nicks
183  *  sss +v      // then opless nicks
184  *  ttt +v      // no ":<mode>": everything stays the same
185  *  aaa -123    // first field with digit: absolute value
186  *  bbb -123
187  *  ccc -125    // only digits, not first field: increment
188  *  ddd -125
189  *  kkk -2 +v   // field with a 'v': absolute value
190  *  lll -4 +v   // only digits: increment
191  *  mmm -4 +v
192  *
193  * Anti net.ride code.
194  *
195  * When the channel already exist, and its TS is larger than
196  * the TS in the BURST message, then we cancel all existing modes.
197  * If its is smaller then the received BURST message is ignored.
198  * If it's equal, then the received modes are just added.
199  *
200  * BURST is also accepted outside a netburst now because it
201  * is sent upstream as reaction to a DESTRUCT message.  For
202  * these BURST messages it is possible that the listed channel
203  * members are already joined.
204  */
205 int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
206 {
207   struct ModeBuf modebuf, *mbuf = 0;
208   struct Channel *chptr;
209   time_t timestamp;
210   struct Membership *member, *nmember;
211   struct Ban *lp, **lp_p;
212   unsigned int parse_flags = (MODE_PARSE_FORCE | MODE_PARSE_BURST);
213   int param, nickpos = 0, banpos = 0;
214   char modestr[BUFSIZE], nickstr[BUFSIZE], banstr[BUFSIZE];
215
216   if (parc < 3)
217     return protocol_violation(sptr,"Too few parameters for BURST");
218
219   if (!(chptr = get_channel(sptr, parv[1], CGT_CREATE)))
220     return 0; /* can't create the channel? */
221
222   timestamp = atoi(parv[2]);
223
224   if (chptr->creationtime)      /* 0 for new (empty) channels,
225                                    i.e. when this server just restarted. */
226   {
227     if (parc == 3)              /* Zannel BURST? */
228     {
229       /* An empty channel without +A set, will cause a BURST message
230          with exactly 3 parameters (because all modes have been reset).
231          If the timestamp on such channels is only a few seconds older
232          from our own, then we ignore this burst: we do not deop our
233          own side.
234          Likewise, we expect the other (empty) side to copy our timestamp
235          from our own BURST message, even though it is slightly larger.
236
237          The reason for this is to allow people to join an empty
238          non-A channel (a zannel) during a net.split, and not be
239          deopped when the net reconnects (with another zannel). When
240          someone joins a split zannel, their side increments the TS by one.
241          If they cycle a few times then we still don't have a reason to
242          deop them. Theoretically I see no reason not to accept ANY timestamp,
243          but to be sure, we only accept timestamps that are just a few
244          seconds off (one second for each time they cycled the channel). */
245
246       /* Don't even deop users who cycled four times during the net.break. */
247       if (timestamp < chptr->creationtime &&
248           chptr->creationtime <= timestamp + 4 &&
249           chptr->users != 0)    /* Only do this when WE have users, so that
250                                    if we do this the BURST that we sent has
251                                    parc > 3 and the other side will use the
252                                    test below: */
253         timestamp = chptr->creationtime; /* Do not deop our side. */
254     }
255     else if (chptr->creationtime < timestamp &&
256              timestamp <= chptr->creationtime + 4 &&
257              chptr->users == 0)
258     {
259       /* If one side of the net.junction does the above
260          timestamp = chptr->creationtime, then the other
261          side must do this: */
262       chptr->creationtime = timestamp;  /* Use the same TS on both sides. */
263     }
264     /* In more complex cases, we might still end up with a
265        creationtime desync of a few seconds, but that should
266        be synced automatically rather quickly (every JOIN
267        caries a timestamp and will sync it; modes by users do
268        not carry timestamps and are accepted regardless).
269        Only when nobody joins the channel on the side with
270        the oldest timestamp before a new net.break occurs
271        precisely inbetween the desync, an unexpected bounce
272        might happen on reconnect. */
273   }
274
275   if (!chptr->creationtime || chptr->creationtime > timestamp) {
276     /*
277      * Kick local members if channel is +i or +k and our TS was larger
278      * than the burst TS (anti net.ride). The modes hack is here because
279      * we have to do this before mode_parse, as chptr may go away.
280      */
281     for (param = 3; param < parc; param++)
282     {
283       int check_modes;
284       if (parv[param][0] != '+')
285         continue;
286       check_modes = netride_modes(parc - param, parv + param, chptr->mode.key);
287       if (check_modes < 0)
288       {
289         if (chptr->users == 0)
290           sub1_from_channel(chptr);
291         return protocol_violation(sptr, "Invalid mode string in BURST");
292       }
293       else if (check_modes)
294       {
295         /* Clear any outstanding rogue invites */
296         mode_invite_clear(chptr);
297         for (member = chptr->members; member; member = nmember)
298         {
299           nmember = member->next_member;
300           if (!MyUser(member->user) || IsZombie(member))
301             continue;
302           /* Kick as netrider if key mismatch *or* remote channel is
303            * +i (unless user is an oper) *or* remote channel is +r
304            * (unless user has an account).
305            */
306           if (!(check_modes & MODE_KEY)
307               && (!(check_modes & MODE_INVITEONLY) || IsAnOper(member->user))
308               && (!(check_modes & MODE_REGONLY) || IsAccount(member->user)))
309             continue;
310           sendcmdto_serv_butone(&me, CMD_KICK, NULL, "%H %C :Net Rider", chptr, member->user);
311           sendcmdto_channel_butserv_butone(&his, CMD_KICK, chptr, NULL, 0, "%H %C :Net Rider", chptr, member->user);
312           make_zombie(member, member->user, &me, &me, chptr);
313         }
314       }
315       break;
316     }
317
318     /* If the channel had only locals, it went away by now. */
319     if (!(chptr = get_channel(sptr, parv[1], CGT_CREATE)))
320       return 0; /* can't create the channel? */
321   }
322
323   /* turn off burst joined flag */
324   for (member = chptr->members; member; member = member->next_member)
325     member->status &= ~(CHFL_BURST_JOINED|CHFL_BURST_ALREADY_OPPED|CHFL_BURST_ALREADY_VOICED);
326
327   if (!chptr->creationtime) /* mark channel as created during BURST */
328     chptr->mode.mode |= MODE_BURSTADDED;
329
330   /* new channel or an older one */
331   if (!chptr->creationtime || chptr->creationtime > timestamp) {
332     chptr->creationtime = timestamp;
333
334     modebuf_init(mbuf = &modebuf, &me, cptr, chptr,
335                  MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY);
336     modebuf_mode(mbuf, MODE_DEL | chptr->mode.mode); /* wipeout modes */
337     chptr->mode.mode &= MODE_BURSTADDED | MODE_WASDELJOINS;
338
339     /* wipe out modes not represented in chptr->mode.mode */
340     if (chptr->mode.limit) {
341       modebuf_mode_uint(mbuf, MODE_DEL | MODE_LIMIT, chptr->mode.limit);
342       chptr->mode.limit = 0;
343     }
344     if (chptr->mode.key[0]) {
345       modebuf_mode_string(mbuf, MODE_DEL | MODE_KEY, chptr->mode.key, 0);
346       chptr->mode.key[0] = '\0';
347     }
348     if (chptr->mode.upass[0]) {
349       modebuf_mode_string(mbuf, MODE_DEL | MODE_UPASS, chptr->mode.upass, 0);
350       chptr->mode.upass[0] = '\0';
351     }
352     if (chptr->mode.apass[0]) {
353       modebuf_mode_string(mbuf, MODE_DEL | MODE_APASS, chptr->mode.apass, 0);
354       chptr->mode.apass[0] = '\0';
355     }
356
357     parse_flags |= (MODE_PARSE_SET | MODE_PARSE_WIPEOUT); /* wipeout keys */
358
359     /* mark bans for wipeout */
360     for (lp = chptr->banlist; lp; lp = lp->next)
361       lp->flags |= BAN_BURST_WIPEOUT;
362
363     /* clear topic set by netrider (if set) */
364     if (*chptr->topic) {
365       *chptr->topic = '\0';
366       *chptr->topic_nick = '\0';
367       chptr->topic_time = 0;
368       sendcmdto_channel_butserv_butone(&his, CMD_TOPIC, chptr, NULL, 0,
369                                        "%H :%s", chptr, chptr->topic);
370     }
371   } else if (chptr->creationtime == timestamp) {
372     modebuf_init(mbuf = &modebuf, &me, cptr, chptr,
373                  MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY);
374
375     parse_flags |= MODE_PARSE_SET; /* set new modes */
376   }
377
378   param = 3; /* parse parameters */
379   while (param < parc) {
380     switch (*parv[param]) {
381     case '+': /* parameter introduces a mode string */
382       param += mode_parse(mbuf, cptr, sptr, chptr, parc - param,
383                           parv + param, parse_flags, NULL);
384       break;
385
386     case '%': /* parameter contains bans */
387       if (parse_flags & MODE_PARSE_SET) {
388         char *banlist = parv[param] + 1, *p = 0, *ban, *ptr;
389         struct Ban *newban;
390
391         for (ban = ircd_strtok(&p, banlist, " "); ban;
392              ban = ircd_strtok(&p, 0, " ")) {
393           ban = collapse(pretty_mask(ban));
394
395             /*
396              * Yeah, we should probably do this elsewhere, and make it better
397              * and more general; this will hold until we get there, though.
398              * I dislike the current add_banid API... -Kev
399              *
400              * I wish there were a better algo. for this than the n^2 one
401              * shown below *sigh*
402              */
403           for (lp = chptr->banlist; lp; lp = lp->next) {
404             if (!ircd_strcmp(lp->banstr, ban)) {
405               ban = 0; /* don't add ban */
406               lp->flags &= ~BAN_BURST_WIPEOUT; /* not wiping out */
407               break; /* new ban already existed; don't even repropagate */
408             } else if (!(lp->flags & BAN_BURST_WIPEOUT) &&
409                        !mmatch(lp->banstr, ban)) {
410               ban = 0; /* don't add ban unless wiping out bans */
411               break; /* new ban is encompassed by an existing one; drop */
412             } else if (!mmatch(ban, lp->banstr))
413               lp->flags |= BAN_OVERLAPPED; /* remove overlapping ban */
414
415             if (!lp->next)
416               break;
417           }
418
419           if (ban) { /* add the new ban to the end of the list */
420             /* Build ban buffer */
421             if (!banpos) {
422               banstr[banpos++] = ' ';
423               banstr[banpos++] = ':';
424               banstr[banpos++] = '%';
425             } else
426               banstr[banpos++] = ' ';
427             for (ptr = ban; *ptr; ptr++) /* add ban to buffer */
428               banstr[banpos++] = *ptr;
429
430             newban = make_ban(ban); /* create new ban */
431             strcpy(newban->who, "*");
432             newban->when = TStime();
433             newban->flags |= BAN_BURSTED;
434             newban->next = 0;
435             if (lp)
436               lp->next = newban; /* link it in */
437             else
438               chptr->banlist = newban;
439           }
440         }
441       } 
442       param++; /* look at next param */
443       break;
444
445     default: /* parameter contains clients */
446       {
447         struct Client *acptr;
448         char *nicklist = parv[param], *p = 0, *nick, *ptr;
449         int current_mode, last_mode, base_mode;
450         int oplevel = -1;       /* Mark first field with digits: means the same as 'o' (but with level). */
451         int last_oplevel = 0;
452         struct Membership* member;
453
454         base_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
455         if (chptr->mode.mode & MODE_DELJOINS)
456             base_mode |= CHFL_DELAYED;
457         current_mode = last_mode = base_mode;
458
459         for (nick = ircd_strtok(&p, nicklist, ","); nick;
460              nick = ircd_strtok(&p, 0, ",")) {
461
462           if ((ptr = strchr(nick, ':'))) { /* new flags; deal */
463             *ptr++ = '\0';
464
465             if (parse_flags & MODE_PARSE_SET) {
466               int current_mode_needs_reset;
467               for (current_mode_needs_reset = 1; *ptr; ptr++) {
468                 if (*ptr == 'o') { /* has oper status */
469                   /*
470                    * An 'o' is pre-oplevel protocol, so this is only for
471                    * backwards compatibility.  Give them an op-level of
472                    * MAXOPLEVEL so everyone can deop them.
473                    */
474                   oplevel = MAXOPLEVEL;
475                   if (current_mode_needs_reset) {
476                     current_mode = base_mode;
477                     current_mode_needs_reset = 0;
478                   }
479                   current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
480                   /*
481                    * Older servers may send XXYYY:ov, in which case we
482                    * do not want to use the code for 'v' below.
483                    */
484                   if (ptr[1] == 'v') {
485                     current_mode |= CHFL_VOICE;
486                     ptr++;
487                   }
488                 }
489                 else if (*ptr == 'v') { /* has voice status */
490                   if (current_mode_needs_reset) {
491                     current_mode = base_mode;
492                     current_mode_needs_reset = 0;
493                   }
494                   current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_VOICE;
495                   oplevel = -1; /* subsequent digits are an absolute op-level value. */
496                 }
497                 else if (IsDigit(*ptr)) {
498                   int level_increment = 0;
499                   if (oplevel == -1) { /* op-level is absolute value? */
500                     if (current_mode_needs_reset) {
501                       current_mode = base_mode;
502                       current_mode_needs_reset = 0;
503                     }
504                     oplevel = 0;
505                   }
506                   current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
507                   do {
508                     level_increment = 10 * level_increment + *ptr++ - '0';
509                   } while (IsDigit(*ptr));
510                   --ptr;
511                   oplevel += level_increment;
512                   if (oplevel > MAXOPLEVEL) {
513                     protocol_violation(sptr, "Invalid cumulative oplevel %u during burst", oplevel);
514                     oplevel = MAXOPLEVEL;
515                     break;
516                   }
517                 }
518                 else { /* I don't recognize that flag */
519                   protocol_violation(sptr, "Invalid flag '%c' in nick part of burst", *ptr);
520                   break; /* so stop processing */
521                 }
522               }
523             }
524           }
525
526           if (!(acptr = findNUser(nick)) || cli_from(acptr) != cptr)
527             continue; /* ignore this client */
528
529           /* Build nick buffer */
530           nickstr[nickpos] = nickpos ? ',' : ' '; /* first char */
531           nickpos++;
532
533           for (ptr = nick; *ptr; ptr++) /* store nick */
534             nickstr[nickpos++] = *ptr;
535
536           if (current_mode != last_mode) { /* if mode changed... */
537             last_mode = current_mode;
538             last_oplevel = oplevel;
539
540             nickstr[nickpos++] = ':'; /* add a specifier */
541             if (current_mode & CHFL_VOICE)
542               nickstr[nickpos++] = 'v';
543             if (current_mode & CHFL_CHANOP)
544             {
545               if (oplevel != MAXOPLEVEL)
546                 nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel);
547               else
548                 nickstr[nickpos++] = 'o';
549             }
550           } else if (current_mode & CHFL_CHANOP && oplevel != last_oplevel) { /* if just op level changed... */
551             nickstr[nickpos++] = ':'; /* add a specifier */
552             nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel - last_oplevel);
553             last_oplevel = oplevel;
554           }
555
556           if (!(member = find_member_link(chptr, acptr)))
557           {
558             add_user_to_channel(chptr, acptr, current_mode, oplevel);
559             if (!(current_mode & CHFL_DELAYED))
560               sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, 0, "%H", chptr);
561           }
562           else
563           {
564             /* The member was already joined (either by CREATE or JOIN).
565                Remember the current mode. */
566             if (member->status & CHFL_CHANOP)
567               member->status |= CHFL_BURST_ALREADY_OPPED;
568             if (member->status & CHFL_VOICE)
569               member->status |= CHFL_BURST_ALREADY_VOICED;
570             /* Synchronize with the burst. */
571             member->status |= CHFL_BURST_JOINED | (current_mode & (CHFL_CHANOP|CHFL_VOICE));
572             SetOpLevel(member, oplevel);
573           }
574         }
575       }
576       param++;
577       break;
578     } /* switch (*parv[param]) */
579   } /* while (param < parc) */
580
581   nickstr[nickpos] = '\0';
582   banstr[banpos] = '\0';
583
584   if (parse_flags & MODE_PARSE_SET) {
585     modebuf_extract(mbuf, modestr + 1); /* for sending BURST onward */
586     modestr[0] = modestr[1] ? ' ' : '\0';
587   } else
588     modestr[0] = '\0';
589
590   sendcmdto_serv_butone(sptr, CMD_BURST, cptr, "%H %Tu%s%s%s", chptr,
591                         chptr->creationtime, modestr, nickstr, banstr);
592
593   if (parse_flags & MODE_PARSE_WIPEOUT || banpos)
594     mode_ban_invalidate(chptr);
595
596   if (parse_flags & MODE_PARSE_SET) { /* any modes changed? */
597     /* first deal with channel members */
598     for (member = chptr->members; member; member = member->next_member) {
599       if (member->status & CHFL_BURST_JOINED) { /* joined during burst */
600         if ((member->status & CHFL_CHANOP) && !(member->status & CHFL_BURST_ALREADY_OPPED))
601           modebuf_mode_client(mbuf, MODE_ADD | CHFL_CHANOP, member->user, OpLevel(member));
602         if ((member->status & CHFL_VOICE) && !(member->status & CHFL_BURST_ALREADY_VOICED))
603           modebuf_mode_client(mbuf, MODE_ADD | CHFL_VOICE, member->user, OpLevel(member));
604       } else if (parse_flags & MODE_PARSE_WIPEOUT) { /* wipeout old ops */
605         if (member->status & CHFL_CHANOP)
606           modebuf_mode_client(mbuf, MODE_DEL | CHFL_CHANOP, member->user, OpLevel(member));
607         if (member->status & CHFL_VOICE)
608           modebuf_mode_client(mbuf, MODE_DEL | CHFL_VOICE, member->user, OpLevel(member));
609         member->status = (member->status
610                           & ~(CHFL_CHANNEL_MANAGER | CHFL_CHANOP | CHFL_VOICE))
611                          | CHFL_DEOPPED;
612       }
613     }
614
615     /* Now deal with channel bans */
616     lp_p = &chptr->banlist;
617     while (*lp_p) {
618       lp = *lp_p;
619
620       /* remove ban from channel */
621       if (lp->flags & (BAN_OVERLAPPED | BAN_BURST_WIPEOUT)) {
622         char *bandup;
623         DupString(bandup, lp->banstr);
624         modebuf_mode_string(mbuf, MODE_DEL | MODE_BAN,
625                             bandup, 1);
626         *lp_p = lp->next; /* clip out of list */
627         free_ban(lp);
628         continue;
629       } else if (lp->flags & BAN_BURSTED) /* add ban to channel */
630         modebuf_mode_string(mbuf, MODE_ADD | MODE_BAN,
631                             lp->banstr, 0); /* don't free banstr */
632
633       lp->flags &= BAN_IPMASK; /* reset the flag */
634       lp_p = &(*lp_p)->next;
635     }
636   }
637
638   return mbuf ? modebuf_flush(mbuf) : 0;
639 }