]> jfr.im git - irc/quakenet/snircd.git/blame - ircd/m_burst.c
Should be unsigned long for A
[irc/quakenet/snircd.git] / ircd / m_burst.c
CommitLineData
189935b1 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 *
37d25209 23 * $Id: m_burst.c,v 1.40.2.2 2006/01/12 03:02:50 entrope Exp $
189935b1 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
109static int
110netride_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 */
203int 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
37d25209 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
189935b1 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 else if (*ptr == 'v') { /* has voice status */
474 if (current_mode_needs_reset) {
475 current_mode = base_mode;
476 current_mode_needs_reset = 0;
477 }
478 current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_VOICE;
479 oplevel = -1; /* subsequent digits are an absolute op-level value. */
480 }
37d25209 481 else if (IsDigit(*ptr)) {
189935b1 482 int level_increment = 0;
483 if (oplevel == -1) { /* op-level is absolute value? */
484 if (current_mode_needs_reset) {
485 current_mode = base_mode;
486 current_mode_needs_reset = 0;
487 }
488 oplevel = 0;
489 }
490 current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
491 do {
492 level_increment = 10 * level_increment + *ptr++ - '0';
37d25209 493 } while (IsDigit(*ptr));
189935b1 494 oplevel += level_increment;
495 }
496 else /* I don't recognize that flag */
497 break; /* so stop processing */
498 }
499 }
500 }
501
502 if (!(acptr = findNUser(nick)) || cli_from(acptr) != cptr)
503 continue; /* ignore this client */
504
505 /* Build nick buffer */
506 nickstr[nickpos] = nickpos ? ',' : ' '; /* first char */
507 nickpos++;
508
509 for (ptr = nick; *ptr; ptr++) /* store nick */
510 nickstr[nickpos++] = *ptr;
511
512 if (current_mode != last_mode) { /* if mode changed... */
513 last_mode = current_mode;
514 last_oplevel = oplevel;
515
516 nickstr[nickpos++] = ':'; /* add a specifier */
517 if (current_mode & CHFL_VOICE)
518 nickstr[nickpos++] = 'v';
519 if (current_mode & CHFL_CHANOP)
520 {
521 if (chptr->mode.apass[0])
522 nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel);
523 else
524 nickstr[nickpos++] = 'o';
525 }
526 } else if (current_mode & CHFL_CHANOP && oplevel != last_oplevel) { /* if just op level changed... */
527 nickstr[nickpos++] = ':'; /* add a specifier */
528 nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel - last_oplevel);
529 last_oplevel = oplevel;
530 }
531
532 if (IsBurst(sptr) || !(member = find_member_link(chptr, acptr)))
533 {
534 add_user_to_channel(chptr, acptr, current_mode, oplevel);
535 if (!(current_mode & CHFL_DELAYED))
536 sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, 0, "%H", chptr);
537 }
538 else
539 {
540 /* The member was already joined (either by CREATE or JOIN).
541 Remember the current mode. */
542 if (member->status & CHFL_CHANOP)
543 member->status |= CHFL_BURST_ALREADY_OPPED;
544 if (member->status & CHFL_VOICE)
545 member->status |= CHFL_BURST_ALREADY_VOICED;
546 /* Synchronize with the burst. */
547 member->status |= CHFL_BURST_JOINED | (current_mode & (CHFL_CHANOP|CHFL_VOICE));
548 SetOpLevel(member, oplevel);
549 }
550 }
551 }
552 param++;
553 break;
554 } /* switch (*parv[param]) */
555 } /* while (param < parc) */
556
557 nickstr[nickpos] = '\0';
558 banstr[banpos] = '\0';
559
560 if (parse_flags & MODE_PARSE_SET) {
561 modebuf_extract(mbuf, modestr + 1); /* for sending BURST onward */
562 modestr[0] = modestr[1] ? ' ' : '\0';
563 } else
564 modestr[0] = '\0';
565
566 sendcmdto_serv_butone(sptr, CMD_BURST, cptr, "%H %Tu%s%s%s", chptr,
567 chptr->creationtime, modestr, nickstr, banstr);
568
569 if (parse_flags & MODE_PARSE_WIPEOUT || banpos)
570 mode_ban_invalidate(chptr);
571
572 if (parse_flags & MODE_PARSE_SET) { /* any modes changed? */
573 /* first deal with channel members */
574 for (member = chptr->members; member; member = member->next_member) {
575 if (member->status & CHFL_BURST_JOINED) { /* joined during burst */
576 if ((member->status & CHFL_CHANOP) && !(member->status & CHFL_BURST_ALREADY_OPPED))
577 modebuf_mode_client(mbuf, MODE_ADD | CHFL_CHANOP, member->user, OpLevel(member));
578 if ((member->status & CHFL_VOICE) && !(member->status & CHFL_BURST_ALREADY_VOICED))
579 modebuf_mode_client(mbuf, MODE_ADD | CHFL_VOICE, member->user, OpLevel(member));
580 } else if (parse_flags & MODE_PARSE_WIPEOUT) { /* wipeout old ops */
581 if (member->status & CHFL_CHANOP)
582 modebuf_mode_client(mbuf, MODE_DEL | CHFL_CHANOP, member->user, OpLevel(member));
583 if (member->status & CHFL_VOICE)
584 modebuf_mode_client(mbuf, MODE_DEL | CHFL_VOICE, member->user, OpLevel(member));
585 member->status = (member->status
586 & ~(CHFL_CHANNEL_MANAGER | CHFL_CHANOP | CHFL_VOICE))
587 | CHFL_DEOPPED;
588 }
589 }
590
591 /* Now deal with channel bans */
592 lp_p = &chptr->banlist;
593 while (*lp_p) {
594 lp = *lp_p;
595
596 /* remove ban from channel */
597 if (lp->flags & (BAN_OVERLAPPED | BAN_BURST_WIPEOUT)) {
598 char *bandup;
599 DupString(bandup, lp->banstr);
600 modebuf_mode_string(mbuf, MODE_DEL | MODE_BAN,
601 bandup, 1);
602 *lp_p = lp->next; /* clip out of list */
603 free_ban(lp);
604 continue;
605 } else if (lp->flags & BAN_BURSTED) /* add ban to channel */
606 modebuf_mode_string(mbuf, MODE_ADD | MODE_BAN,
607 lp->banstr, 0); /* don't free banstr */
608
609 lp->flags &= BAN_IPMASK; /* reset the flag */
610 lp_p = &(*lp_p)->next;
611 }
612 }
613
614 return mbuf ? modebuf_flush(mbuf) : 0;
615}