2 * IRC - Internet Relay Chat, ircd/m_burst.c
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Computing Center
6 * See file AUTHORS in IRC package for additional names of
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)
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.
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.
23 * $Id: m_burst.c 1861 2008-01-03 00:07:21Z klmitch $
27 * m_functions execute protocol messages on this server:
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...).
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.
39 * (!IsServer(cptr)) => (cptr == sptr), because
40 * prefixes are taken *only* from servers...
43 * (sptr == cptr) => the message didn't
46 * (sptr != cptr && IsServer(sptr) means
47 * the prefix specified servername. (?)
49 * (sptr != cptr && !IsServer(sptr) means
50 * that message originated from a remote
55 * (!IsServer(sptr)) means that, sptr can safely
56 * taken as defining the target structure of the
57 * message in this server.
59 * *Always* true (if 'parse' and others are working correct):
61 * 1) sptr->from == cptr (note: cptr->from == cptr)
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 ]
68 * parc number of variable parameter strings (if zero,
69 * parv is allowed to be NULL)
71 * parv a NULL terminated list of parameter pointers,
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*
79 * note: it is guaranteed that parv[0]..parv[parc-1] are all
88 #include "ircd_alloc.h"
89 #include "ircd_features.h"
91 #include "ircd_reply.h"
92 #include "ircd_string.h"
102 #include "ircd_snprintf.h"
104 /* #include <assert.h> -- Now using assert in ircd_log.h */
110 netride_modes(int parc, char **parv, const char *curr_key)
112 char *modes = parv[0];
115 assert(modes && modes[0] == '+');
121 result |= MODE_INVITEONLY;
124 if (strcmp(curr_key, *++parv))
131 result |= MODE_REGONLY;
139 * ms_burst - server message handler
141 * -- by Run carlo@runaway.xs4all.nl december 1995 till march 1997
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
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
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
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').
174 * "A8 B #test 87654321 +ntkAl key secret 123 A8AAG,A8AAC:v,A8AAA:0,A8AAF:2,A8AAD,A8AAB:v1,A8AAE:1 :%ban1 ban2"
176 * <mode> list example:
178 * "xxx,sss:v,ttt,aaa:123,bbb,ccc:2,ddd,kkk:v2,lll:2,mmm"
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
187 * ccc -125 // only digits, not first field: increment
189 * kkk -2 +v // field with a 'v': absolute value
190 * lll -4 +v // only digits: increment
193 * Anti net.ride code.
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.
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.
205 int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
207 struct ModeBuf modebuf, *mbuf = 0;
208 struct Channel *chptr;
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];
217 return protocol_violation(sptr,"Too few parameters for BURST");
219 if (!(chptr = get_channel(sptr, parv[1], CGT_CREATE)))
220 return 0; /* can't create the channel? */
222 timestamp = atoi(parv[2]);
224 if (chptr->creationtime) /* 0 for new (empty) channels,
225 i.e. when this server just restarted. */
227 if (parc == 3) /* Zannel BURST? */
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
234 Likewise, we expect the other (empty) side to copy our timestamp
235 from our own BURST message, even though it is slightly larger.
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). */
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
253 timestamp = chptr->creationtime; /* Do not deop our side. */
255 else if (chptr->creationtime < timestamp &&
256 timestamp <= chptr->creationtime + 4 &&
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. */
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. */
275 if (!chptr->creationtime || chptr->creationtime > timestamp) {
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.
281 for (param = 3; param < parc; param++)
284 if (parv[param][0] != '+')
286 check_modes = netride_modes(parc - param, parv + param, chptr->mode.key);
289 if (chptr->users == 0)
290 sub1_from_channel(chptr);
291 return protocol_violation(sptr, "Invalid mode string in BURST");
293 else if (check_modes)
295 /* Clear any outstanding rogue invites */
296 mode_invite_clear(chptr);
297 for (member = chptr->members; member; member = nmember)
299 nmember = member->next_member;
300 if (!MyUser(member->user) || IsZombie(member))
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).
306 if (!(check_modes & MODE_KEY)
307 && (!(check_modes & MODE_INVITEONLY) || IsAnOper(member->user))
308 && (!(check_modes & MODE_REGONLY) || IsAccount(member->user)))
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);
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? */
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);
327 if (!chptr->creationtime) /* mark channel as created during BURST */
328 chptr->mode.mode |= MODE_BURSTADDED;
330 /* new channel or an older one */
331 if (!chptr->creationtime || chptr->creationtime > timestamp) {
332 chptr->creationtime = timestamp;
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;
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;
344 if (chptr->mode.access) {
345 modebuf_mode_uint(mbuf, MODE_DEL | MODE_ACCESS, chptr->mode.access);
346 chptr->mode.access = 0;
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';
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';
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';
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';
365 parse_flags |= (MODE_PARSE_SET | MODE_PARSE_WIPEOUT); /* wipeout keys */
367 /* mark bans for wipeout */
368 for (lp = chptr->banlist; lp; lp = lp->next)
369 lp->flags |= BAN_BURST_WIPEOUT;
371 /* clear topic set by netrider (if set) */
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);
379 if (*chptr->chanowner) {
380 *chptr->chanowner = '\0';
382 } else if (chptr->creationtime == timestamp) {
383 modebuf_init(mbuf = &modebuf, &me, cptr, chptr,
384 MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY);
386 parse_flags |= MODE_PARSE_SET; /* set new modes */
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);
397 case '%': /* parameter contains bans */
398 if (parse_flags & MODE_PARSE_SET) {
399 char *banlist = parv[param] + 1, *p = 0, *ban, *ptr;
402 for (ban = ircd_strtok(&p, banlist, " "); ban;
403 ban = ircd_strtok(&p, 0, " ")) {
404 ban = collapse(pretty_mask(ban));
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
411 * I wish there were a better algo. for this than the n^2 one
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 */
430 if (ban) { /* add the new ban to the end of the list */
431 /* Build ban buffer */
433 banstr[banpos++] = ' ';
434 banstr[banpos++] = ':';
435 banstr[banpos++] = '%';
437 banstr[banpos++] = ' ';
438 for (ptr = ban; *ptr; ptr++) /* add ban to buffer */
439 banstr[banpos++] = *ptr;
441 newban = make_ban(ban); /* create new ban */
442 strcpy(newban->who, "*");
443 newban->when = TStime();
444 newban->flags |= BAN_BURSTED;
447 lp->next = newban; /* link it in */
449 chptr->banlist = newban;
453 param++; /* look at next param */
456 default: /* parameter contains clients */
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;
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;
470 for (nick = ircd_strtok(&p, nicklist, ","); nick;
471 nick = ircd_strtok(&p, 0, ",")) {
473 if ((ptr = strchr(nick, ':'))) { /* new flags; deal */
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 */
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.
485 oplevel = MAXOPLEVEL;
486 if (current_mode_needs_reset) {
487 current_mode = base_mode;
488 current_mode_needs_reset = 0;
490 current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
492 * Older servers may send XXYYY:ov, in which case we
493 * do not want to use the code for 'v' below.
496 current_mode |= CHFL_VOICE;
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;
505 current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_VOICE;
506 oplevel = -1; /* subsequent digits are an absolute op-level value. */
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;
513 current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_HALFOP;
514 oplevel = -1; /* subsequent digits are an absolute op-level value. */
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;
521 current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_INVISIBLE;
522 oplevel = -1; /* subsequent digits are an absolute op-level value. */
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;
533 current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
535 level_increment = 10 * level_increment + *ptr++ - '0';
536 } while (IsDigit(*ptr));
538 oplevel += level_increment;
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 */
548 if (!(acptr = findNUser(nick)) || cli_from(acptr) != cptr)
549 continue; /* ignore this client */
551 /* Build nick buffer */
552 nickstr[nickpos] = nickpos ? ',' : ' '; /* first char */
555 for (ptr = nick; *ptr; ptr++) /* store nick */
556 nickstr[nickpos++] = *ptr;
558 if (current_mode != last_mode) { /* if mode changed... */
559 last_mode = current_mode;
560 last_oplevel = oplevel;
562 nickstr[nickpos++] = ':'; /* add a specifier */
563 if (current_mode & CHFL_VOICE)
564 nickstr[nickpos++] = 'v';
565 if (current_mode & CHFL_CHANOP)
567 if (chptr->mode.apass[0])
568 nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel);
570 nickstr[nickpos++] = 'o';
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;
578 if (!(member = find_member_link(chptr, acptr)))
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);
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);
602 } /* switch (*parv[param]) */
603 } /* while (param < parc) */
605 nickstr[nickpos] = '\0';
606 banstr[banpos] = '\0';
608 if (parse_flags & MODE_PARSE_SET) {
609 modebuf_extract(mbuf, modestr + 1); /* for sending BURST onward */
610 modestr[0] = modestr[1] ? ' ' : '\0';
614 sendcmdto_serv_butone(sptr, CMD_BURST, cptr, "%H %Tu%s%s%s", chptr,
615 chptr->creationtime, modestr, nickstr, banstr);
617 if (parse_flags & MODE_PARSE_WIPEOUT || banpos)
618 mode_ban_invalidate(chptr);
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))
643 /* Now deal with channel bans */
644 lp_p = &chptr->banlist;
648 /* remove ban from channel */
649 if (lp->flags & (BAN_OVERLAPPED | BAN_BURST_WIPEOUT)) {
651 DupString(bandup, lp->banstr);
652 modebuf_mode_string(mbuf, MODE_DEL | MODE_BAN,
654 *lp_p = lp->next; /* clip out of list */
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 */
661 lp->flags &= BAN_IPMASK; /* reset the flag */
662 lp_p = &(*lp_p)->next;
666 return mbuf ? modebuf_flush(mbuf) : 0;