fixed ssl.c bug when ssl backend returns IO_BLOCKED but IO engine doesn't get informe...
[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: m_burst.c 1861 2008-01-03 00:07:21Z klmitch $
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.access) {
345       modebuf_mode_uint(mbuf, MODE_DEL | MODE_ACCESS, chptr->mode.access);
346       chptr->mode.access = 0;
347     }
348     if (chptr->mode.key[0]) {
349       modebuf_mode_string(mbuf, MODE_DEL | MODE_KEY, chptr->mode.key, 0);
350       chptr->mode.key[0] = '\0';
351     }
352         if (chptr->mode.altchan[0]) {
353       modebuf_mode_string(mbuf, MODE_DEL | MODE_ALTCHAN, chptr->mode.altchan, 0);
354       chptr->mode.altchan[0] = '\0';
355     }
356     if (chptr->mode.upass[0]) {
357       modebuf_mode_string(mbuf, MODE_DEL | MODE_UPASS, chptr->mode.upass, 0);
358       chptr->mode.upass[0] = '\0';
359     }
360     if (chptr->mode.apass[0]) {
361       modebuf_mode_string(mbuf, MODE_DEL | MODE_APASS, chptr->mode.apass, 0);
362       chptr->mode.apass[0] = '\0';
363     }
364
365     parse_flags |= (MODE_PARSE_SET | MODE_PARSE_WIPEOUT); /* wipeout keys */
366
367     /* mark bans for wipeout */
368     for (lp = chptr->banlist; lp; lp = lp->next)
369       lp->flags |= BAN_BURST_WIPEOUT;
370
371     /* clear topic set by netrider (if set) */
372     if (*chptr->topic) {
373       *chptr->topic = '\0';
374       *chptr->topic_nick = '\0';
375       chptr->topic_time = 0;
376       sendcmdto_channel_butserv_butone(&his, CMD_TOPIC, chptr, NULL, 0,
377                                        "%H :%s", chptr, chptr->topic);
378     }
379         if (*chptr->chanowner) {
380           *chptr->chanowner = '\0';
381         }
382   } else if (chptr->creationtime == timestamp) {
383     modebuf_init(mbuf = &modebuf, &me, cptr, chptr,
384                  MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY);
385
386     parse_flags |= MODE_PARSE_SET; /* set new modes */
387   }
388
389   param = 3; /* parse parameters */
390   while (param < parc) {
391     switch (*parv[param]) {
392     case '+': /* parameter introduces a mode string */
393       param += mode_parse(mbuf, cptr, sptr, chptr, parc - param,
394                           parv + param, parse_flags, NULL);
395       break;
396
397     case '%': /* parameter contains bans */
398       if (parse_flags & MODE_PARSE_SET) {
399         char *banlist = parv[param] + 1, *p = 0, *ban, *ptr;
400         struct Ban *newban;
401
402         for (ban = ircd_strtok(&p, banlist, " "); ban;
403              ban = ircd_strtok(&p, 0, " ")) {
404           ban = collapse(pretty_mask(ban));
405
406             /*
407              * Yeah, we should probably do this elsewhere, and make it better
408              * and more general; this will hold until we get there, though.
409              * I dislike the current add_banid API... -Kev
410              *
411              * I wish there were a better algo. for this than the n^2 one
412              * shown below *sigh*
413              */
414           for (lp = chptr->banlist; lp; lp = lp->next) {
415             if (!ircd_strcmp(lp->banstr, ban)) {
416               ban = 0; /* don't add ban */
417               lp->flags &= ~BAN_BURST_WIPEOUT; /* not wiping out */
418               break; /* new ban already existed; don't even repropagate */
419             } else if (!(lp->flags & BAN_BURST_WIPEOUT) &&
420                        !mmatch(lp->banstr, ban)) {
421               ban = 0; /* don't add ban unless wiping out bans */
422               break; /* new ban is encompassed by an existing one; drop */
423             } else if (!mmatch(ban, lp->banstr))
424               lp->flags |= BAN_OVERLAPPED; /* remove overlapping ban */
425
426             if (!lp->next)
427               break;
428           }
429
430           if (ban) { /* add the new ban to the end of the list */
431             /* Build ban buffer */
432             if (!banpos) {
433               banstr[banpos++] = ' ';
434               banstr[banpos++] = ':';
435               banstr[banpos++] = '%';
436             } else
437               banstr[banpos++] = ' ';
438             for (ptr = ban; *ptr; ptr++) /* add ban to buffer */
439               banstr[banpos++] = *ptr;
440
441             newban = make_ban(ban); /* create new ban */
442             strcpy(newban->who, "*");
443             newban->when = TStime();
444             newban->flags |= BAN_BURSTED;
445             newban->next = 0;
446             if (lp)
447               lp->next = newban; /* link it in */
448             else
449               chptr->banlist = newban;
450           }
451         }
452       } 
453       param++; /* look at next param */
454       break;
455
456     default: /* parameter contains clients */
457       {
458         struct Client *acptr;
459         char *nicklist = parv[param], *p = 0, *nick, *ptr;
460         int current_mode, last_mode, base_mode;
461         int oplevel = -1;       /* Mark first field with digits: means the same as 'o' (but with level). */
462         int last_oplevel = 0;
463         struct Membership* member;
464
465         base_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
466         if (chptr->mode.mode & MODE_DELJOINS)
467             base_mode |= CHFL_DELAYED;
468         current_mode = last_mode = base_mode;
469
470         for (nick = ircd_strtok(&p, nicklist, ","); nick;
471              nick = ircd_strtok(&p, 0, ",")) {
472
473           if ((ptr = strchr(nick, ':'))) { /* new flags; deal */
474             *ptr++ = '\0';
475
476             if (parse_flags & MODE_PARSE_SET) {
477               int current_mode_needs_reset;
478               for (current_mode_needs_reset = 1; *ptr; ptr++) {
479                 if (*ptr == 'o') { /* has oper status */
480                   /*
481                    * An 'o' is pre-oplevel protocol, so this is only for
482                    * backwards compatibility.  Give them an op-level of
483                    * MAXOPLEVEL so everyone can deop them.
484                    */
485                   oplevel = MAXOPLEVEL;
486                   if (current_mode_needs_reset) {
487                     current_mode = base_mode;
488                     current_mode_needs_reset = 0;
489                   }
490                   current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
491                   /*
492                    * Older servers may send XXYYY:ov, in which case we
493                    * do not want to use the code for 'v' below.
494                    */
495                   if (ptr[1] == 'v') {
496                     current_mode |= CHFL_VOICE;
497                     ptr++;
498                   }
499                 }
500                 else if (*ptr == 'v') { /* has voice status */
501                   if (current_mode_needs_reset) {
502                     current_mode = base_mode;
503                     current_mode_needs_reset = 0;
504                   }
505                   current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_VOICE;
506                   oplevel = -1; /* subsequent digits are an absolute op-level value. */
507                 }
508         else if (*ptr == 'h') { /* has halfop status */
509                   if (current_mode_needs_reset) {
510                     current_mode = base_mode;
511                     current_mode_needs_reset = 0;
512                   }
513                   current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_HALFOP;
514                   oplevel = -1; /* subsequent digits are an absolute op-level value. */
515                 }
516         else if (*ptr == 'i') { /* has voice status */
517                   if (current_mode_needs_reset) {
518                     current_mode = base_mode;
519                     current_mode_needs_reset = 0;
520                   }
521                   current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_INVISIBLE;
522                   oplevel = -1; /* subsequent digits are an absolute op-level value. */
523                 }
524                 else if (IsDigit(*ptr)) {
525                   int level_increment = 0;
526                   if (oplevel == -1) { /* op-level is absolute value? */
527                     if (current_mode_needs_reset) {
528                       current_mode = base_mode;
529                       current_mode_needs_reset = 0;
530                     }
531                     oplevel = 0;
532                   }
533                   current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
534                   do {
535                     level_increment = 10 * level_increment + *ptr++ - '0';
536                   } while (IsDigit(*ptr));
537                   --ptr;
538                   oplevel += level_increment;
539                 }
540                 else { /* I don't recognize that flag */
541                   protocol_violation(sptr, "Invalid flag '%c' in nick part of burst", *ptr);
542                   break; /* so stop processing */
543                 }
544               }
545             }
546           }
547
548           if (!(acptr = findNUser(nick)) || cli_from(acptr) != cptr)
549             continue; /* ignore this client */
550
551           /* Build nick buffer */
552           nickstr[nickpos] = nickpos ? ',' : ' '; /* first char */
553           nickpos++;
554
555           for (ptr = nick; *ptr; ptr++) /* store nick */
556             nickstr[nickpos++] = *ptr;
557
558           if (current_mode != last_mode) { /* if mode changed... */
559             last_mode = current_mode;
560             last_oplevel = oplevel;
561
562             nickstr[nickpos++] = ':'; /* add a specifier */
563             if (current_mode & CHFL_VOICE)
564               nickstr[nickpos++] = 'v';
565             if (current_mode & CHFL_CHANOP)
566             {
567               if (chptr->mode.apass[0])
568                 nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel);
569               else
570                 nickstr[nickpos++] = 'o';
571             }
572           } else if (current_mode & CHFL_CHANOP && oplevel != last_oplevel) { /* if just op level changed... */
573             nickstr[nickpos++] = ':'; /* add a specifier */
574             nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel - last_oplevel);
575             last_oplevel = oplevel;
576           }
577
578           if (!(member = find_member_link(chptr, acptr)))
579           {
580             add_user_to_channel(chptr, acptr, current_mode, oplevel);
581             if (!(current_mode & CHFL_DELAYED))
582               sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, 0, "%H", chptr);
583           }
584           else
585           {
586             /* The member was already joined (either by CREATE or JOIN).
587                Remember the current mode. */
588             if (member->status & CHFL_CHANOP)
589               member->status |= CHFL_BURST_ALREADY_OPPED;
590         if (member->status & CHFL_HALFOP)
591               member->status |= CHFL_BURST_ALREADY_HALFOPPED;
592             if (member->status & CHFL_VOICE)
593               member->status |= CHFL_BURST_ALREADY_VOICED;
594             /* Synchronize with the burst. */
595             member->status |= CHFL_BURST_JOINED | (current_mode & (CHFL_CHANOP|CHFL_VOICE));
596             SetOpLevel(member, oplevel);
597           }
598         }
599       }
600       param++;
601       break;
602     } /* switch (*parv[param]) */
603   } /* while (param < parc) */
604
605   nickstr[nickpos] = '\0';
606   banstr[banpos] = '\0';
607
608   if (parse_flags & MODE_PARSE_SET) {
609     modebuf_extract(mbuf, modestr + 1); /* for sending BURST onward */
610     modestr[0] = modestr[1] ? ' ' : '\0';
611   } else
612     modestr[0] = '\0';
613
614   sendcmdto_serv_butone(sptr, CMD_BURST, cptr, "%H %Tu%s%s%s", chptr,
615                         chptr->creationtime, modestr, nickstr, banstr);
616
617   if (parse_flags & MODE_PARSE_WIPEOUT || banpos)
618     mode_ban_invalidate(chptr);
619
620   if (parse_flags & MODE_PARSE_SET) { /* any modes changed? */
621     /* first deal with channel members */
622     for (member = chptr->members; member; member = member->next_member) {
623       if (member->status & CHFL_BURST_JOINED) { /* joined during burst */
624         if ((member->status & CHFL_CHANOP) && !(member->status & CHFL_BURST_ALREADY_OPPED))
625           modebuf_mode_client(mbuf, MODE_ADD | CHFL_CHANOP, member->user, OpLevel(member));
626     if ((member->status & CHFL_HALFOP) && !(member->status & CHFL_BURST_ALREADY_HALFOPPED))
627           modebuf_mode_client(mbuf, MODE_ADD | CHFL_HALFOP, member->user, OpLevel(member));
628         if ((member->status & CHFL_VOICE) && !(member->status & CHFL_BURST_ALREADY_VOICED))
629           modebuf_mode_client(mbuf, MODE_ADD | CHFL_VOICE, member->user, OpLevel(member));
630       } else if (parse_flags & MODE_PARSE_WIPEOUT) { /* wipeout old ops */
631         if (member->status & CHFL_CHANOP)
632           modebuf_mode_client(mbuf, MODE_DEL | CHFL_CHANOP, member->user, OpLevel(member));
633     if (member->status & CHFL_HALFOP)
634           modebuf_mode_client(mbuf, MODE_DEL | CHFL_HALFOP, member->user, OpLevel(member));
635         if (member->status & CHFL_VOICE)
636           modebuf_mode_client(mbuf, MODE_DEL | CHFL_VOICE, member->user, OpLevel(member));
637         member->status = (member->status
638                           & ~(CHFL_CHANNEL_MANAGER | CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE))
639                          | CHFL_DEOPPED;
640       }
641     }
642
643     /* Now deal with channel bans */
644     lp_p = &chptr->banlist;
645     while (*lp_p) {
646       lp = *lp_p;
647
648       /* remove ban from channel */
649       if (lp->flags & (BAN_OVERLAPPED | BAN_BURST_WIPEOUT)) {
650         char *bandup;
651         DupString(bandup, lp->banstr);
652         modebuf_mode_string(mbuf, MODE_DEL | MODE_BAN,
653                             bandup, 1);
654         *lp_p = lp->next; /* clip out of list */
655         free_ban(lp);
656         continue;
657       } else if (lp->flags & BAN_BURSTED) /* add ban to channel */
658         modebuf_mode_string(mbuf, MODE_ADD | MODE_BAN,
659                             lp->banstr, 0); /* don't free banstr */
660
661       lp->flags &= BAN_IPMASK; /* reset the flag */
662       lp_p = &(*lp_p)->next;
663     }
664   }
665
666   return mbuf ? modebuf_flush(mbuf) : 0;
667 }