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.
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 /* wipeout any limit and keys that are set */
340 parse_flags |= (MODE_PARSE_SET | MODE_PARSE_WIPEOUT);
342 /* mark bans for wipeout */
343 for (lp = chptr->banlist; lp; lp = lp->next)
344 lp->flags |= BAN_BURST_WIPEOUT;
346 /* clear topic set by netrider (if set) */
348 *chptr->topic = '\0';
349 *chptr->topic_nick = '\0';
350 chptr->topic_time = 0;
351 sendcmdto_channel_butserv_butone(&his, CMD_TOPIC, chptr, NULL, 0,
352 "%H :%s", chptr, chptr->topic);
354 } else if (chptr->creationtime == timestamp) {
355 modebuf_init(mbuf = &modebuf, &me, cptr, chptr,
356 MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY);
358 parse_flags |= MODE_PARSE_SET; /* set new modes */
361 param = 3; /* parse parameters */
362 while (param < parc) {
363 switch (*parv[param]) {
364 case '+': /* parameter introduces a mode string */
365 param += mode_parse(mbuf, cptr, sptr, chptr, parc - param,
366 parv + param, parse_flags, NULL);
369 case '%': /* parameter contains bans */
370 if (parse_flags & MODE_PARSE_SET) {
371 char *banlist = parv[param] + 1, *p = 0, *ban, *ptr;
374 for (ban = ircd_strtok(&p, banlist, " "); ban;
375 ban = ircd_strtok(&p, 0, " ")) {
376 ban = collapse(pretty_mask(ban));
379 * Yeah, we should probably do this elsewhere, and make it better
380 * and more general; this will hold until we get there, though.
381 * I dislike the current add_banid API... -Kev
383 * I wish there were a better algo. for this than the n^2 one
386 for (lp = chptr->banlist; lp; lp = lp->next) {
387 if (!ircd_strcmp(lp->banstr, ban)) {
388 ban = 0; /* don't add ban */
389 lp->flags &= ~BAN_BURST_WIPEOUT; /* not wiping out */
390 break; /* new ban already existed; don't even repropagate */
391 } else if (!(lp->flags & BAN_BURST_WIPEOUT) &&
392 !mmatch(lp->banstr, ban)) {
393 ban = 0; /* don't add ban unless wiping out bans */
394 break; /* new ban is encompassed by an existing one; drop */
395 } else if (!mmatch(ban, lp->banstr))
396 lp->flags |= BAN_OVERLAPPED; /* remove overlapping ban */
402 if (ban) { /* add the new ban to the end of the list */
403 /* Build ban buffer */
405 banstr[banpos++] = ' ';
406 banstr[banpos++] = ':';
407 banstr[banpos++] = '%';
409 banstr[banpos++] = ' ';
410 for (ptr = ban; *ptr; ptr++) /* add ban to buffer */
411 banstr[banpos++] = *ptr;
413 newban = make_ban(ban); /* create new ban */
414 strcpy(newban->who, "*");
415 newban->when = TStime();
416 newban->flags |= BAN_BURSTED;
419 lp->next = newban; /* link it in */
421 chptr->banlist = newban;
425 param++; /* look at next param */
428 default: /* parameter contains clients */
430 struct Client *acptr;
431 char *nicklist = parv[param], *p = 0, *nick, *ptr;
432 int current_mode, last_mode, base_mode;
433 int oplevel = -1; /* Mark first field with digits: means the same as 'o' (but with level). */
434 int last_oplevel = 0;
435 struct Membership* member;
437 base_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
438 if (chptr->mode.mode & MODE_DELJOINS)
439 base_mode |= CHFL_DELAYED;
440 current_mode = last_mode = base_mode;
442 for (nick = ircd_strtok(&p, nicklist, ","); nick;
443 nick = ircd_strtok(&p, 0, ",")) {
445 if ((ptr = strchr(nick, ':'))) { /* new flags; deal */
448 if (parse_flags & MODE_PARSE_SET) {
449 int current_mode_needs_reset;
450 for (current_mode_needs_reset = 1; *ptr; ptr++) {
451 if (*ptr == 'o') { /* has oper status */
453 * An 'o' is pre-oplevel protocol, so this is only for
454 * backwards compatibility. Give them an op-level of
455 * MAXOPLEVEL so everyone can deop them.
457 oplevel = MAXOPLEVEL;
458 if (current_mode_needs_reset) {
459 current_mode = base_mode;
460 current_mode_needs_reset = 0;
462 current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
464 * Older servers may send XXYYY:ov, in which case we
465 * do not want to use the code for 'v' below.
468 current_mode |= CHFL_VOICE;
472 else if (*ptr == 'v') { /* has voice status */
473 if (current_mode_needs_reset) {
474 current_mode = base_mode;
475 current_mode_needs_reset = 0;
477 current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_VOICE;
478 oplevel = -1; /* subsequent digits are an absolute op-level value. */
480 else if (IsDigit(*ptr)) {
481 int level_increment = 0;
482 if (oplevel == -1) { /* op-level is absolute value? */
483 if (current_mode_needs_reset) {
484 current_mode = base_mode;
485 current_mode_needs_reset = 0;
489 current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
491 level_increment = 10 * level_increment + *ptr++ - '0';
492 } while (IsDigit(*ptr));
494 oplevel += level_increment;
495 if (oplevel > MAXOPLEVEL) {
496 protocol_violation(sptr, "Invalid cumulative oplevel %u during burst", oplevel);
497 oplevel = MAXOPLEVEL;
501 else { /* I don't recognize that flag */
502 protocol_violation(sptr, "Invalid flag '%c' in nick part of burst", *ptr);
503 break; /* so stop processing */
509 if (!(acptr = findNUser(nick)) || cli_from(acptr) != cptr)
510 continue; /* ignore this client */
512 /* Build nick buffer */
513 nickstr[nickpos] = nickpos ? ',' : ' '; /* first char */
516 for (ptr = nick; *ptr; ptr++) /* store nick */
517 nickstr[nickpos++] = *ptr;
519 if (current_mode != last_mode) { /* if mode changed... */
520 last_mode = current_mode;
521 last_oplevel = oplevel;
523 nickstr[nickpos++] = ':'; /* add a specifier */
524 if (current_mode & CHFL_VOICE)
525 nickstr[nickpos++] = 'v';
526 if (current_mode & CHFL_CHANOP)
528 if (oplevel != MAXOPLEVEL)
529 nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel);
531 nickstr[nickpos++] = 'o';
533 } else if (current_mode & CHFL_CHANOP && oplevel != last_oplevel) { /* if just op level changed... */
534 nickstr[nickpos++] = ':'; /* add a specifier */
535 nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel - last_oplevel);
536 last_oplevel = oplevel;
539 if (!(member = find_member_link(chptr, acptr)))
541 add_user_to_channel(chptr, acptr, current_mode, oplevel);
542 if (!(current_mode & CHFL_DELAYED))
543 sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, 0, "%H", chptr);
547 /* The member was already joined (either by CREATE or JOIN).
548 Remember the current mode. */
549 if (member->status & CHFL_CHANOP)
550 member->status |= CHFL_BURST_ALREADY_OPPED;
551 if (member->status & CHFL_VOICE)
552 member->status |= CHFL_BURST_ALREADY_VOICED;
553 /* Synchronize with the burst. */
554 member->status |= CHFL_BURST_JOINED | (current_mode & (CHFL_CHANOP|CHFL_VOICE));
555 SetOpLevel(member, oplevel);
561 } /* switch (*parv[param]) */
562 } /* while (param < parc) */
564 nickstr[nickpos] = '\0';
565 banstr[banpos] = '\0';
567 if (parse_flags & MODE_PARSE_SET) {
568 modebuf_extract(mbuf, modestr + 1); /* for sending BURST onward */
569 modestr[0] = modestr[1] ? ' ' : '\0';
573 sendcmdto_serv_butone(sptr, CMD_BURST, cptr, "%H %Tu%s%s%s", chptr,
574 chptr->creationtime, modestr, nickstr, banstr);
576 if (parse_flags & MODE_PARSE_WIPEOUT || banpos)
577 mode_ban_invalidate(chptr);
579 if (parse_flags & MODE_PARSE_SET) { /* any modes changed? */
580 /* first deal with channel members */
581 for (member = chptr->members; member; member = member->next_member) {
582 if (member->status & CHFL_BURST_JOINED) { /* joined during burst */
583 if ((member->status & CHFL_CHANOP) && !(member->status & CHFL_BURST_ALREADY_OPPED))
584 modebuf_mode_client(mbuf, MODE_ADD | CHFL_CHANOP, member->user, OpLevel(member));
585 if ((member->status & CHFL_VOICE) && !(member->status & CHFL_BURST_ALREADY_VOICED))
586 modebuf_mode_client(mbuf, MODE_ADD | CHFL_VOICE, member->user, OpLevel(member));
587 } else if (parse_flags & MODE_PARSE_WIPEOUT) { /* wipeout old ops */
588 if (member->status & CHFL_CHANOP)
589 modebuf_mode_client(mbuf, MODE_DEL | CHFL_CHANOP, member->user, OpLevel(member));
590 if (member->status & CHFL_VOICE)
591 modebuf_mode_client(mbuf, MODE_DEL | CHFL_VOICE, member->user, OpLevel(member));
592 member->status = (member->status
593 & ~(CHFL_CHANNEL_MANAGER | CHFL_CHANOP | CHFL_VOICE))
598 /* Now deal with channel bans */
599 lp_p = &chptr->banlist;
603 /* remove ban from channel */
604 if (lp->flags & (BAN_OVERLAPPED | BAN_BURST_WIPEOUT)) {
606 DupString(bandup, lp->banstr);
607 modebuf_mode_string(mbuf, MODE_DEL | MODE_BAN,
609 *lp_p = lp->next; /* clip out of list */
612 } else if (lp->flags & BAN_BURSTED) /* add ban to channel */
613 modebuf_mode_string(mbuf, MODE_ADD | MODE_BAN,
614 lp->banstr, 0); /* don't free banstr */
616 lp->flags &= BAN_IPMASK; /* reset the flag */
617 lp_p = &(*lp_p)->next;
621 return mbuf ? modebuf_flush(mbuf) : 0;