Properly handle member modes like XXYYY:ov in bursts.
[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 'i':
119       result |= MODE_INVITEONLY;
120       break;
121     case 'k':
122       if (strcmp(curr_key, *++parv))
123         result |= MODE_KEY;
124       break;
125     case 'l':
126       ++parv;
127       break;
128     case 'r':
129       result |= MODE_REGONLY;
130       break;
131     }
132   }
133   return result;
134 }
135
136 /*
137  * ms_burst - server message handler
138  *
139  * --  by Run carlo@runaway.xs4all.nl  december 1995 till march 1997
140  *
141  * parv[0] = sender prefix
142  * parv[1] = channel name
143  * parv[2] = channel timestamp
144  * The meaning of the following parv[]'s depend on their first character:
145  * If parv[n] starts with a '+':
146  * Net burst, additive modes
147  *   parv[n] = <mode>
148  *   parv[n+1] = <param> (optional)
149  *   parv[n+2] = <param> (optional)
150  * If parv[n] starts with a '%', then n will be parc-1:
151  *   parv[n] = %<ban> <ban> <ban> ...
152  * If parv[n] starts with another character:
153  *   parv[n] = <nick>[:<mode>],<nick>[:<mode>],...
154  *   where <mode> defines the mode and op-level
155  *   for nick and all following nicks until the
156  *   next <mode> field.
157  *   Digits in the <mode> field have of two meanings:
158  *   1) if it is the first field in this BURST message
159  *      that contains digits, and/or when a 'v' is
160  *      present in the <mode>:
161  *      The absolute value of the op-level.
162  *   2) if there are only digits in this field and
163  *      it is not the first field with digits:
164  *      An op-level increment relative to the previous
165  *      op-level.
166  *   First all modeless nicks must be emmitted,
167  *   then all combinations of modes without ops
168  *   (currently that is only 'v') followed by the same
169  *   series but then with ops (currently 'o','ov').
170  *
171  * Example:
172  * "A8 B #test 87654321 +ntkAl key secret 123 A8AAG,A8AAC:v,A8AAA:0,A8AAF:2,A8AAD,A8AAB:v1,A8AAE:1 :%ban1 ban2"
173  *
174  * <mode> list example:
175  *
176  * "xxx,sss:v,ttt,aaa:123,bbb,ccc:2,ddd,kkk:v2,lll:2,mmm"
177  *
178  * means
179  *
180  *  xxx         // first modeless nicks
181  *  sss +v      // then opless nicks
182  *  ttt +v      // no ":<mode>": everything stays the same
183  *  aaa -123    // first field with digit: absolute value
184  *  bbb -123
185  *  ccc -125    // only digits, not first field: increment
186  *  ddd -125
187  *  kkk -2 +v   // field with a 'v': absolute value
188  *  lll -4 +v   // only digits: increment
189  *  mmm -4 +v
190  *
191  * Anti net.ride code.
192  *
193  * When the channel already exist, and its TS is larger than
194  * the TS in the BURST message, then we cancel all existing modes.
195  * If its is smaller then the received BURST message is ignored.
196  * If it's equal, then the received modes are just added.
197  *
198  * BURST is also accepted outside a netburst now because it
199  * is sent upstream as reaction to a DESTRUCT message.  For
200  * these BURST messages it is possible that the listed channel
201  * members are already joined.
202  */
203 int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
204 {
205   struct ModeBuf modebuf, *mbuf = 0;
206   struct Channel *chptr;
207   time_t timestamp;
208   struct Membership *member, *nmember;
209   struct Ban *lp, **lp_p;
210   unsigned int parse_flags = (MODE_PARSE_FORCE | MODE_PARSE_BURST);
211   int param, nickpos = 0, banpos = 0;
212   char modestr[BUFSIZE], nickstr[BUFSIZE], banstr[BUFSIZE];
213
214   if (parc < 3)
215     return protocol_violation(sptr,"Too few parameters for BURST");
216
217   if (!(chptr = get_channel(sptr, parv[1], CGT_CREATE)))
218     return 0; /* can't create the channel? */
219
220   timestamp = atoi(parv[2]);
221
222   if (chptr->creationtime)      /* 0 for new (empty) channels,
223                                    i.e. when this server just restarted. */
224   {
225     if (parc == 3)              /* Zannel BURST? */
226     {
227       /* An empty channel without +A set, will cause a BURST message
228          with exactly 3 parameters (because all modes have been reset).
229          If the timestamp on such channels is only a few seconds older
230          from our own, then we ignore this burst: we do not deop our
231          own side.
232          Likewise, we expect the other (empty) side to copy our timestamp
233          from our own BURST message, even though it is slightly larger.
234
235          The reason for this is to allow people to join an empty
236          non-A channel (a zannel) during a net.split, and not be
237          deopped when the net reconnects (with another zannel). When
238          someone joins a split zannel, their side increments the TS by one.
239          If they cycle a few times then we still don't have a reason to
240          deop them. Theoretically I see no reason not to accept ANY timestamp,
241          but to be sure, we only accept timestamps that are just a few
242          seconds off (one second for each time they cycled the channel). */
243
244       /* Don't even deop users who cycled four times during the net.break. */
245       if (timestamp < chptr->creationtime &&
246           chptr->creationtime <= timestamp + 4 &&
247           chptr->users != 0)    /* Only do this when WE have users, so that
248                                    if we do this the BURST that we sent has
249                                    parc > 3 and the other side will use the
250                                    test below: */
251         timestamp = chptr->creationtime; /* Do not deop our side. */
252     }
253     else if (chptr->creationtime < timestamp &&
254              timestamp <= chptr->creationtime + 4 &&
255              chptr->users == 0)
256     {
257       /* If one side of the net.junction does the above
258          timestamp = chptr->creationtime, then the other
259          side must do this: */
260       chptr->creationtime = timestamp;  /* Use the same TS on both sides. */
261     }
262     /* In more complex cases, we might still end up with a
263        creationtime desync of a few seconds, but that should
264        be synced automatically rather quickly (every JOIN
265        caries a timestamp and will sync it; modes by users do
266        not carry timestamps and are accepted regardless).
267        Only when nobody joins the channel on the side with
268        the oldest timestamp before a new net.break occurs
269        precisely inbetween the desync, an unexpected bounce
270        might happen on reconnect. */
271   }
272
273   if (!chptr->creationtime || chptr->creationtime > timestamp) {
274     /*
275      * Kick local members if channel is +i or +k and our TS was larger
276      * than the burst TS (anti net.ride). The modes hack is here because
277      * we have to do this before mode_parse, as chptr may go away.
278      */
279     for (param = 3; param < parc; param++)
280     {
281       int check_modes;
282       if (parv[param][0] != '+')
283         continue;
284       check_modes = netride_modes(parc - param, parv + param, chptr->mode.key);
285       if (check_modes)
286       {
287         /* Clear any outstanding rogue invites */
288         mode_invite_clear(chptr);
289         for (member = chptr->members; member; member = nmember)
290         {
291           nmember = member->next_member;
292           if (!MyUser(member->user) || IsZombie(member))
293             continue;
294           /* Kick as netrider if key mismatch *or* remote channel is
295            * +i (unless user is an oper) *or* remote channel is +r
296            * (unless user has an account).
297            */
298           if (!(check_modes & MODE_KEY)
299               && (!(check_modes & MODE_INVITEONLY) || IsAnOper(member->user))
300               && (!(check_modes & MODE_REGONLY) || IsAccount(member->user)))
301             continue;
302           sendcmdto_serv_butone(&me, CMD_KICK, NULL, "%H %C :Net Rider", chptr, member->user);
303           sendcmdto_channel_butserv_butone(&his, CMD_KICK, chptr, NULL, 0, "%H %C :Net Rider", chptr, member->user);
304           make_zombie(member, member->user, &me, &me, chptr);
305         }
306       }
307       break;
308     }
309
310     /* If the channel had only locals, it went away by now. */
311     if (!(chptr = get_channel(sptr, parv[1], CGT_CREATE)))
312       return 0; /* can't create the channel? */
313   }
314
315   /* turn off burst joined flag */
316   for (member = chptr->members; member; member = member->next_member)
317     member->status &= ~(CHFL_BURST_JOINED|CHFL_BURST_ALREADY_OPPED|CHFL_BURST_ALREADY_VOICED);
318
319   if (!chptr->creationtime) /* mark channel as created during BURST */
320     chptr->mode.mode |= MODE_BURSTADDED;
321
322   /* new channel or an older one */
323   if (!chptr->creationtime || chptr->creationtime > timestamp) {
324     chptr->creationtime = timestamp;
325
326     modebuf_init(mbuf = &modebuf, &me, cptr, chptr,
327                  MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY);
328     modebuf_mode(mbuf, MODE_DEL | chptr->mode.mode); /* wipeout modes */
329     chptr->mode.mode &= MODE_BURSTADDED | MODE_WASDELJOINS;
330
331     /* wipe out modes not represented in chptr->mode.mode */
332     if (chptr->mode.limit) {
333       modebuf_mode_uint(mbuf, MODE_DEL | MODE_LIMIT, chptr->mode.limit);
334       chptr->mode.limit = 0;
335     }
336     if (chptr->mode.key[0]) {
337       modebuf_mode_string(mbuf, MODE_DEL | MODE_KEY, chptr->mode.key, 0);
338       chptr->mode.key[0] = '\0';
339     }
340     if (chptr->mode.upass[0]) {
341       modebuf_mode_string(mbuf, MODE_DEL | MODE_UPASS, chptr->mode.upass, 0);
342       chptr->mode.upass[0] = '\0';
343     }
344     if (chptr->mode.apass[0]) {
345       modebuf_mode_string(mbuf, MODE_DEL | MODE_APASS, chptr->mode.apass, 0);
346       chptr->mode.apass[0] = '\0';
347     }
348
349     parse_flags |= (MODE_PARSE_SET | MODE_PARSE_WIPEOUT); /* wipeout keys */
350
351     /* mark bans for wipeout */
352     for (lp = chptr->banlist; lp; lp = lp->next)
353       lp->flags |= BAN_BURST_WIPEOUT;
354
355     /* clear topic set by netrider (if set) */
356     if (*chptr->topic) {
357       *chptr->topic = '\0';
358       *chptr->topic_nick = '\0';
359       chptr->topic_time = 0;
360       sendcmdto_channel_butserv_butone(&his, CMD_TOPIC, chptr, NULL, 0,
361                                        "%H :%s", chptr, chptr->topic);
362     }
363   } else if (chptr->creationtime == timestamp) {
364     modebuf_init(mbuf = &modebuf, &me, cptr, chptr,
365                  MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY);
366
367     parse_flags |= MODE_PARSE_SET; /* set new modes */
368   }
369
370   param = 3; /* parse parameters */
371   while (param < parc) {
372     switch (*parv[param]) {
373     case '+': /* parameter introduces a mode string */
374       param += mode_parse(mbuf, cptr, sptr, chptr, parc - param,
375                           parv + param, parse_flags, NULL);
376       break;
377
378     case '%': /* parameter contains bans */
379       if (parse_flags & MODE_PARSE_SET) {
380         char *banlist = parv[param] + 1, *p = 0, *ban, *ptr;
381         struct Ban *newban;
382
383         for (ban = ircd_strtok(&p, banlist, " "); ban;
384              ban = ircd_strtok(&p, 0, " ")) {
385           ban = collapse(pretty_mask(ban));
386
387             /*
388              * Yeah, we should probably do this elsewhere, and make it better
389              * and more general; this will hold until we get there, though.
390              * I dislike the current add_banid API... -Kev
391              *
392              * I wish there were a better algo. for this than the n^2 one
393              * shown below *sigh*
394              */
395           for (lp = chptr->banlist; lp; lp = lp->next) {
396             if (!ircd_strcmp(lp->banstr, ban)) {
397               ban = 0; /* don't add ban */
398               lp->flags &= ~BAN_BURST_WIPEOUT; /* not wiping out */
399               break; /* new ban already existed; don't even repropagate */
400             } else if (!(lp->flags & BAN_BURST_WIPEOUT) &&
401                        !mmatch(lp->banstr, ban)) {
402               ban = 0; /* don't add ban unless wiping out bans */
403               break; /* new ban is encompassed by an existing one; drop */
404             } else if (!mmatch(ban, lp->banstr))
405               lp->flags |= BAN_OVERLAPPED; /* remove overlapping ban */
406
407             if (!lp->next)
408               break;
409           }
410
411           if (ban) { /* add the new ban to the end of the list */
412             /* Build ban buffer */
413             if (!banpos) {
414               banstr[banpos++] = ' ';
415               banstr[banpos++] = ':';
416               banstr[banpos++] = '%';
417             } else
418               banstr[banpos++] = ' ';
419             for (ptr = ban; *ptr; ptr++) /* add ban to buffer */
420               banstr[banpos++] = *ptr;
421
422             newban = make_ban(ban); /* create new ban */
423             strcpy(newban->who, "*");
424             newban->when = TStime();
425             newban->flags |= BAN_BURSTED;
426             newban->next = 0;
427             if (lp)
428               lp->next = newban; /* link it in */
429             else
430               chptr->banlist = newban;
431           }
432         }
433       } 
434       param++; /* look at next param */
435       break;
436
437     default: /* parameter contains clients */
438       {
439         struct Client *acptr;
440         char *nicklist = parv[param], *p = 0, *nick, *ptr;
441         int current_mode, last_mode, base_mode;
442         int oplevel = -1;       /* Mark first field with digits: means the same as 'o' (but with level). */
443         int last_oplevel = 0;
444         struct Membership* member;
445
446         base_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
447         if (chptr->mode.mode & MODE_DELJOINS)
448             base_mode |= CHFL_DELAYED;
449         current_mode = last_mode = base_mode;
450
451         for (nick = ircd_strtok(&p, nicklist, ","); nick;
452              nick = ircd_strtok(&p, 0, ",")) {
453
454           if ((ptr = strchr(nick, ':'))) { /* new flags; deal */
455             *ptr++ = '\0';
456
457             if (parse_flags & MODE_PARSE_SET) {
458               int current_mode_needs_reset;
459               for (current_mode_needs_reset = 1; *ptr; ptr++) {
460                 if (*ptr == 'o') { /* has oper status */
461                   /*
462                    * An 'o' is pre-oplevel protocol, so this is only for
463                    * backwards compatibility.  Give them an op-level of
464                    * MAXOPLEVEL so everyone can deop them.
465                    */
466                   oplevel = MAXOPLEVEL;
467                   if (current_mode_needs_reset) {
468                     current_mode = base_mode;
469                     current_mode_needs_reset = 0;
470                   }
471                   current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
472                   /*
473                    * Older servers may send XXYYY:ov, in which case we
474                    * do not want to use the code for 'v' below.
475                    */
476                   if (ptr[1] == 'v') {
477                     current_mode |= CHFL_VOICE;
478                     ptr++;
479                   }
480                 }
481                 else if (*ptr == 'v') { /* has voice status */
482                   if (current_mode_needs_reset) {
483                     current_mode = base_mode;
484                     current_mode_needs_reset = 0;
485                   }
486                   current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_VOICE;
487                   oplevel = -1; /* subsequent digits are an absolute op-level value. */
488                 }
489                 else if (IsDigit(*ptr)) {
490                   int level_increment = 0;
491                   if (oplevel == -1) { /* op-level is absolute value? */
492                     if (current_mode_needs_reset) {
493                       current_mode = base_mode;
494                       current_mode_needs_reset = 0;
495                     }
496                     oplevel = 0;
497                   }
498                   current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
499                   do {
500                     level_increment = 10 * level_increment + *ptr++ - '0';
501                   } while (IsDigit(*ptr));
502                   oplevel += level_increment;
503                 }
504                 else /* I don't recognize that flag */
505                   break; /* so stop processing */
506               }
507             }
508           }
509
510           if (!(acptr = findNUser(nick)) || cli_from(acptr) != cptr)
511             continue; /* ignore this client */
512
513           /* Build nick buffer */
514           nickstr[nickpos] = nickpos ? ',' : ' '; /* first char */
515           nickpos++;
516
517           for (ptr = nick; *ptr; ptr++) /* store nick */
518             nickstr[nickpos++] = *ptr;
519
520           if (current_mode != last_mode) { /* if mode changed... */
521             last_mode = current_mode;
522             last_oplevel = oplevel;
523
524             nickstr[nickpos++] = ':'; /* add a specifier */
525             if (current_mode & CHFL_VOICE)
526               nickstr[nickpos++] = 'v';
527             if (current_mode & CHFL_CHANOP)
528             {
529               if (chptr->mode.apass[0])
530                 nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel);
531               else
532                 nickstr[nickpos++] = 'o';
533             }
534           } else if (current_mode & CHFL_CHANOP && oplevel != last_oplevel) { /* if just op level changed... */
535             nickstr[nickpos++] = ':'; /* add a specifier */
536             nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel - last_oplevel);
537             last_oplevel = oplevel;
538           }
539
540           if (IsBurst(sptr) || !(member = find_member_link(chptr, acptr)))
541           {
542             add_user_to_channel(chptr, acptr, current_mode, oplevel);
543             if (!(current_mode & CHFL_DELAYED))
544               sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, 0, "%H", chptr);
545           }
546           else
547           {
548             /* The member was already joined (either by CREATE or JOIN).
549                Remember the current mode. */
550             if (member->status & CHFL_CHANOP)
551               member->status |= CHFL_BURST_ALREADY_OPPED;
552             if (member->status & CHFL_VOICE)
553               member->status |= CHFL_BURST_ALREADY_VOICED;
554             /* Synchronize with the burst. */
555             member->status |= CHFL_BURST_JOINED | (current_mode & (CHFL_CHANOP|CHFL_VOICE));
556             SetOpLevel(member, oplevel);
557           }
558         }
559       }
560       param++;
561       break;
562     } /* switch (*parv[param]) */
563   } /* while (param < parc) */
564
565   nickstr[nickpos] = '\0';
566   banstr[banpos] = '\0';
567
568   if (parse_flags & MODE_PARSE_SET) {
569     modebuf_extract(mbuf, modestr + 1); /* for sending BURST onward */
570     modestr[0] = modestr[1] ? ' ' : '\0';
571   } else
572     modestr[0] = '\0';
573
574   sendcmdto_serv_butone(sptr, CMD_BURST, cptr, "%H %Tu%s%s%s", chptr,
575                         chptr->creationtime, modestr, nickstr, banstr);
576
577   if (parse_flags & MODE_PARSE_WIPEOUT || banpos)
578     mode_ban_invalidate(chptr);
579
580   if (parse_flags & MODE_PARSE_SET) { /* any modes changed? */
581     /* first deal with channel members */
582     for (member = chptr->members; member; member = member->next_member) {
583       if (member->status & CHFL_BURST_JOINED) { /* joined during burst */
584         if ((member->status & CHFL_CHANOP) && !(member->status & CHFL_BURST_ALREADY_OPPED))
585           modebuf_mode_client(mbuf, MODE_ADD | CHFL_CHANOP, member->user, OpLevel(member));
586         if ((member->status & CHFL_VOICE) && !(member->status & CHFL_BURST_ALREADY_VOICED))
587           modebuf_mode_client(mbuf, MODE_ADD | CHFL_VOICE, member->user, OpLevel(member));
588       } else if (parse_flags & MODE_PARSE_WIPEOUT) { /* wipeout old ops */
589         if (member->status & CHFL_CHANOP)
590           modebuf_mode_client(mbuf, MODE_DEL | CHFL_CHANOP, member->user, OpLevel(member));
591         if (member->status & CHFL_VOICE)
592           modebuf_mode_client(mbuf, MODE_DEL | CHFL_VOICE, member->user, OpLevel(member));
593         member->status = (member->status
594                           & ~(CHFL_CHANNEL_MANAGER | CHFL_CHANOP | CHFL_VOICE))
595                          | CHFL_DEOPPED;
596       }
597     }
598
599     /* Now deal with channel bans */
600     lp_p = &chptr->banlist;
601     while (*lp_p) {
602       lp = *lp_p;
603
604       /* remove ban from channel */
605       if (lp->flags & (BAN_OVERLAPPED | BAN_BURST_WIPEOUT)) {
606         char *bandup;
607         DupString(bandup, lp->banstr);
608         modebuf_mode_string(mbuf, MODE_DEL | MODE_BAN,
609                             bandup, 1);
610         *lp_p = lp->next; /* clip out of list */
611         free_ban(lp);
612         continue;
613       } else if (lp->flags & BAN_BURSTED) /* add ban to channel */
614         modebuf_mode_string(mbuf, MODE_ADD | MODE_BAN,
615                             lp->banstr, 0); /* don't free banstr */
616
617       lp->flags &= BAN_IPMASK; /* reset the flag */
618       lp_p = &(*lp_p)->next;
619     }
620   }
621
622   return mbuf ? modebuf_flush(mbuf) : 0;
623 }