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 /* 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.key[0]) {
345 modebuf_mode_string(mbuf, MODE_DEL | MODE_KEY, chptr->mode.key, 0);
346 chptr->mode.key[0] = '\0';
348 if (chptr->mode.upass[0]) {
349 modebuf_mode_string(mbuf, MODE_DEL | MODE_UPASS, chptr->mode.upass, 0);
350 chptr->mode.upass[0] = '\0';
352 if (chptr->mode.apass[0]) {
353 modebuf_mode_string(mbuf, MODE_DEL | MODE_APASS, chptr->mode.apass, 0);
354 chptr->mode.apass[0] = '\0';
357 parse_flags |= (MODE_PARSE_SET | MODE_PARSE_WIPEOUT); /* wipeout keys */
359 /* mark bans for wipeout */
360 for (lp = chptr->banlist; lp; lp = lp->next)
361 lp->flags |= BAN_BURST_WIPEOUT;
363 /* clear topic set by netrider (if set) */
365 *chptr->topic = '\0';
366 *chptr->topic_nick = '\0';
367 chptr->topic_time = 0;
368 sendcmdto_channel_butserv_butone(&his, CMD_TOPIC, chptr, NULL, 0,
369 "%H :%s", chptr, chptr->topic);
371 } else if (chptr->creationtime == timestamp) {
372 modebuf_init(mbuf = &modebuf, &me, cptr, chptr,
373 MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY);
375 parse_flags |= MODE_PARSE_SET; /* set new modes */
378 param = 3; /* parse parameters */
379 while (param < parc) {
380 switch (*parv[param]) {
381 case '+': /* parameter introduces a mode string */
382 param += mode_parse(mbuf, cptr, sptr, chptr, parc - param,
383 parv + param, parse_flags, NULL);
386 case '%': /* parameter contains bans */
387 if (parse_flags & MODE_PARSE_SET) {
388 char *banlist = parv[param] + 1, *p = 0, *ban, *ptr;
391 for (ban = ircd_strtok(&p, banlist, " "); ban;
392 ban = ircd_strtok(&p, 0, " ")) {
393 ban = collapse(pretty_mask(ban));
396 * Yeah, we should probably do this elsewhere, and make it better
397 * and more general; this will hold until we get there, though.
398 * I dislike the current add_banid API... -Kev
400 * I wish there were a better algo. for this than the n^2 one
403 for (lp = chptr->banlist; lp; lp = lp->next) {
404 if (!ircd_strcmp(lp->banstr, ban)) {
405 ban = 0; /* don't add ban */
406 lp->flags &= ~BAN_BURST_WIPEOUT; /* not wiping out */
407 break; /* new ban already existed; don't even repropagate */
408 } else if (!(lp->flags & BAN_BURST_WIPEOUT) &&
409 !mmatch(lp->banstr, ban)) {
410 ban = 0; /* don't add ban unless wiping out bans */
411 break; /* new ban is encompassed by an existing one; drop */
412 } else if (!mmatch(ban, lp->banstr))
413 lp->flags |= BAN_OVERLAPPED; /* remove overlapping ban */
419 if (ban) { /* add the new ban to the end of the list */
420 /* Build ban buffer */
422 banstr[banpos++] = ' ';
423 banstr[banpos++] = ':';
424 banstr[banpos++] = '%';
426 banstr[banpos++] = ' ';
427 for (ptr = ban; *ptr; ptr++) /* add ban to buffer */
428 banstr[banpos++] = *ptr;
430 newban = make_ban(ban); /* create new ban */
431 strcpy(newban->who, "*");
432 newban->when = TStime();
433 newban->flags |= BAN_BURSTED;
436 lp->next = newban; /* link it in */
438 chptr->banlist = newban;
442 param++; /* look at next param */
445 default: /* parameter contains clients */
447 struct Client *acptr;
448 char *nicklist = parv[param], *p = 0, *nick, *ptr;
449 int current_mode, last_mode, base_mode;
450 int oplevel = -1; /* Mark first field with digits: means the same as 'o' (but with level). */
451 int last_oplevel = 0;
452 struct Membership* member;
454 base_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
455 if (chptr->mode.mode & MODE_DELJOINS)
456 base_mode |= CHFL_DELAYED;
457 current_mode = last_mode = base_mode;
459 for (nick = ircd_strtok(&p, nicklist, ","); nick;
460 nick = ircd_strtok(&p, 0, ",")) {
462 if ((ptr = strchr(nick, ':'))) { /* new flags; deal */
465 if (parse_flags & MODE_PARSE_SET) {
466 int current_mode_needs_reset;
467 for (current_mode_needs_reset = 1; *ptr; ptr++) {
468 if (*ptr == 'o') { /* has oper status */
470 * An 'o' is pre-oplevel protocol, so this is only for
471 * backwards compatibility. Give them an op-level of
472 * MAXOPLEVEL so everyone can deop them.
474 oplevel = MAXOPLEVEL;
475 if (current_mode_needs_reset) {
476 current_mode = base_mode;
477 current_mode_needs_reset = 0;
479 current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
481 * Older servers may send XXYYY:ov, in which case we
482 * do not want to use the code for 'v' below.
485 current_mode |= CHFL_VOICE;
489 else if (*ptr == 'v') { /* has voice status */
490 if (current_mode_needs_reset) {
491 current_mode = base_mode;
492 current_mode_needs_reset = 0;
494 current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_VOICE;
495 oplevel = -1; /* subsequent digits are an absolute op-level value. */
497 else if (IsDigit(*ptr)) {
498 int level_increment = 0;
499 if (oplevel == -1) { /* op-level is absolute value? */
500 if (current_mode_needs_reset) {
501 current_mode = base_mode;
502 current_mode_needs_reset = 0;
506 current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
508 level_increment = 10 * level_increment + *ptr++ - '0';
509 } while (IsDigit(*ptr));
511 oplevel += level_increment;
513 else { /* I don't recognize that flag */
514 protocol_violation(sptr, "Invalid flag '%c' in nick part of burst", *ptr);
515 break; /* so stop processing */
521 if (!(acptr = findNUser(nick)) || cli_from(acptr) != cptr)
522 continue; /* ignore this client */
524 /* Build nick buffer */
525 nickstr[nickpos] = nickpos ? ',' : ' '; /* first char */
528 for (ptr = nick; *ptr; ptr++) /* store nick */
529 nickstr[nickpos++] = *ptr;
531 if (current_mode != last_mode) { /* if mode changed... */
532 last_mode = current_mode;
533 last_oplevel = oplevel;
535 nickstr[nickpos++] = ':'; /* add a specifier */
536 if (current_mode & CHFL_VOICE)
537 nickstr[nickpos++] = 'v';
538 if (current_mode & CHFL_CHANOP)
540 if (chptr->mode.apass[0])
541 nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel);
543 nickstr[nickpos++] = 'o';
545 } else if (current_mode & CHFL_CHANOP && oplevel != last_oplevel) { /* if just op level changed... */
546 nickstr[nickpos++] = ':'; /* add a specifier */
547 nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel - last_oplevel);
548 last_oplevel = oplevel;
551 if (!(member = find_member_link(chptr, acptr)))
553 add_user_to_channel(chptr, acptr, current_mode, oplevel);
554 if (!(current_mode & CHFL_DELAYED))
555 sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, 0, "%H", chptr);
559 /* The member was already joined (either by CREATE or JOIN).
560 Remember the current mode. */
561 if (member->status & CHFL_CHANOP)
562 member->status |= CHFL_BURST_ALREADY_OPPED;
563 if (member->status & CHFL_VOICE)
564 member->status |= CHFL_BURST_ALREADY_VOICED;
565 /* Synchronize with the burst. */
566 member->status |= CHFL_BURST_JOINED | (current_mode & (CHFL_CHANOP|CHFL_VOICE));
567 SetOpLevel(member, oplevel);
573 } /* switch (*parv[param]) */
574 } /* while (param < parc) */
576 nickstr[nickpos] = '\0';
577 banstr[banpos] = '\0';
579 if (parse_flags & MODE_PARSE_SET) {
580 modebuf_extract(mbuf, modestr + 1); /* for sending BURST onward */
581 modestr[0] = modestr[1] ? ' ' : '\0';
585 sendcmdto_serv_butone(sptr, CMD_BURST, cptr, "%H %Tu%s%s%s", chptr,
586 chptr->creationtime, modestr, nickstr, banstr);
588 if (parse_flags & MODE_PARSE_WIPEOUT || banpos)
589 mode_ban_invalidate(chptr);
591 if (parse_flags & MODE_PARSE_SET) { /* any modes changed? */
592 /* first deal with channel members */
593 for (member = chptr->members; member; member = member->next_member) {
594 if (member->status & CHFL_BURST_JOINED) { /* joined during burst */
595 if ((member->status & CHFL_CHANOP) && !(member->status & CHFL_BURST_ALREADY_OPPED))
596 modebuf_mode_client(mbuf, MODE_ADD | CHFL_CHANOP, member->user, OpLevel(member));
597 if ((member->status & CHFL_VOICE) && !(member->status & CHFL_BURST_ALREADY_VOICED))
598 modebuf_mode_client(mbuf, MODE_ADD | CHFL_VOICE, member->user, OpLevel(member));
599 } else if (parse_flags & MODE_PARSE_WIPEOUT) { /* wipeout old ops */
600 if (member->status & CHFL_CHANOP)
601 modebuf_mode_client(mbuf, MODE_DEL | CHFL_CHANOP, member->user, OpLevel(member));
602 if (member->status & CHFL_VOICE)
603 modebuf_mode_client(mbuf, MODE_DEL | CHFL_VOICE, member->user, OpLevel(member));
604 member->status = (member->status
605 & ~(CHFL_CHANNEL_MANAGER | CHFL_CHANOP | CHFL_VOICE))
610 /* Now deal with channel bans */
611 lp_p = &chptr->banlist;
615 /* remove ban from channel */
616 if (lp->flags & (BAN_OVERLAPPED | BAN_BURST_WIPEOUT)) {
618 DupString(bandup, lp->banstr);
619 modebuf_mode_string(mbuf, MODE_DEL | MODE_BAN,
621 *lp_p = lp->next; /* clip out of list */
624 } else if (lp->flags & BAN_BURSTED) /* add ban to channel */
625 modebuf_mode_string(mbuf, MODE_ADD | MODE_BAN,
626 lp->banstr, 0); /* don't free banstr */
628 lp->flags &= BAN_IPMASK; /* reset the flag */
629 lp_p = &(*lp_p)->next;
633 return mbuf ? modebuf_flush(mbuf) : 0;