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,v 1.40.2.3 2007/01/13 18:47:19 entrope Exp $
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] == '+');
119 result
|= MODE_INVITEONLY
;
122 if (strcmp(curr_key
, *++parv
))
129 result
|= MODE_REGONLY
;
137 * ms_burst - server message handler
139 * -- by Run carlo@runaway.xs4all.nl december 1995 till march 1997
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
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
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
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').
172 * "A8 B #test 87654321 +ntkAl key secret 123 A8AAG,A8AAC:v,A8AAA:0,A8AAF:2,A8AAD,A8AAB:v1,A8AAE:1 :%ban1 ban2"
174 * <mode> list example:
176 * "xxx,sss:v,ttt,aaa:123,bbb,ccc:2,ddd,kkk:v2,lll:2,mmm"
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
185 * ccc -125 // only digits, not first field: increment
187 * kkk -2 +v // field with a 'v': absolute value
188 * lll -4 +v // only digits: increment
191 * Anti net.ride code.
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.
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.
203 int ms_burst(struct Client
*cptr
, struct Client
*sptr
, int parc
, char *parv
[])
205 struct ModeBuf modebuf
, *mbuf
= 0;
206 struct Channel
*chptr
;
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
];
215 return protocol_violation(sptr
,"Too few parameters for BURST");
217 if (!(chptr
= get_channel(sptr
, parv
[1], CGT_CREATE
)))
218 return 0; /* can't create the channel? */
220 timestamp
= atoi(parv
[2]);
222 if (chptr
->creationtime
) /* 0 for new (empty) channels,
223 i.e. when this server just restarted. */
225 if (parc
== 3) /* Zannel BURST? */
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
232 Likewise, we expect the other (empty) side to copy our timestamp
233 from our own BURST message, even though it is slightly larger.
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). */
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
251 timestamp
= chptr
->creationtime
; /* Do not deop our side. */
253 else if (chptr
->creationtime
< timestamp
&&
254 timestamp
<= chptr
->creationtime
+ 4 &&
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. */
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. */
273 if (!chptr
->creationtime
|| chptr
->creationtime
> timestamp
) {
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.
279 for (param
= 3; param
< parc
; param
++)
282 if (parv
[param
][0] != '+')
284 check_modes
= netride_modes(parc
- param
, parv
+ param
, chptr
->mode
.key
);
287 /* Clear any outstanding rogue invites */
288 mode_invite_clear(chptr
);
289 for (member
= chptr
->members
; member
; member
= nmember
)
291 nmember
= member
->next_member
;
292 if (!MyUser(member
->user
) || IsZombie(member
))
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).
298 if (!(check_modes
& MODE_KEY
)
299 && (!(check_modes
& MODE_INVITEONLY
) || IsAnOper(member
->user
))
300 && (!(check_modes
& MODE_REGONLY
) || IsAccount(member
->user
)))
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
);
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? */
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
);
319 if (!chptr
->creationtime
) /* mark channel as created during BURST */
320 chptr
->mode
.mode
|= MODE_BURSTADDED
;
322 /* new channel or an older one */
323 if (!chptr
->creationtime
|| chptr
->creationtime
> timestamp
) {
324 chptr
->creationtime
= timestamp
;
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
;
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;
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';
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';
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';
349 parse_flags
|= (MODE_PARSE_SET
| MODE_PARSE_WIPEOUT
); /* wipeout keys */
351 /* mark bans for wipeout */
352 for (lp
= chptr
->banlist
; lp
; lp
= lp
->next
)
353 lp
->flags
|= BAN_BURST_WIPEOUT
;
355 /* clear topic set by netrider (if set) */
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
);
363 } else if (chptr
->creationtime
== timestamp
) {
364 modebuf_init(mbuf
= &modebuf
, &me
, cptr
, chptr
,
365 MODEBUF_DEST_CHANNEL
| MODEBUF_DEST_NOKEY
);
367 parse_flags
|= MODE_PARSE_SET
; /* set new modes */
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
);
378 case '%': /* parameter contains bans */
379 if (parse_flags
& MODE_PARSE_SET
) {
380 char *banlist
= parv
[param
] + 1, *p
= 0, *ban
, *ptr
;
383 for (ban
= ircd_strtok(&p
, banlist
, " "); ban
;
384 ban
= ircd_strtok(&p
, 0, " ")) {
385 ban
= collapse(pretty_mask(ban
));
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
392 * I wish there were a better algo. for this than the n^2 one
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 */
411 if (ban
) { /* add the new ban to the end of the list */
412 /* Build ban buffer */
414 banstr
[banpos
++] = ' ';
415 banstr
[banpos
++] = ':';
416 banstr
[banpos
++] = '%';
418 banstr
[banpos
++] = ' ';
419 for (ptr
= ban
; *ptr
; ptr
++) /* add ban to buffer */
420 banstr
[banpos
++] = *ptr
;
422 newban
= make_ban(ban
); /* create new ban */
423 strcpy(newban
->who
, "*");
424 newban
->when
= TStime();
425 newban
->flags
|= BAN_BURSTED
;
428 lp
->next
= newban
; /* link it in */
430 chptr
->banlist
= newban
;
434 param
++; /* look at next param */
437 default: /* parameter contains clients */
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
;
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
;
451 for (nick
= ircd_strtok(&p
, nicklist
, ","); nick
;
452 nick
= ircd_strtok(&p
, 0, ",")) {
454 if ((ptr
= strchr(nick
, ':'))) { /* new flags; deal */
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 */
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.
466 oplevel
= MAXOPLEVEL
;
467 if (current_mode_needs_reset
) {
468 current_mode
= base_mode
;
469 current_mode_needs_reset
= 0;
471 current_mode
= (current_mode
& ~(CHFL_DEOPPED
| CHFL_DELAYED
)) | CHFL_CHANOP
;
473 * Older servers may send XXYYY:ov, in which case we
474 * do not want to use the code for 'v' below.
477 current_mode
|= CHFL_VOICE
;
481 else if (*ptr
== 'v') { /* has voice status */
482 if (current_mode_needs_reset
) {
483 current_mode
= base_mode
;
484 current_mode_needs_reset
= 0;
486 current_mode
= (current_mode
& ~CHFL_DELAYED
) | CHFL_VOICE
;
487 oplevel
= -1; /* subsequent digits are an absolute op-level value. */
489 else if (IsDigit(*ptr
)) {
490 int level_increment
= 0;
491 if (oplevel
== -1) { /* op-level is absolute value? */
492 if (current_mode_needs_reset
) {
493 current_mode
= base_mode
;
494 current_mode_needs_reset
= 0;
498 current_mode
= (current_mode
& ~(CHFL_DEOPPED
| CHFL_DELAYED
)) | CHFL_CHANOP
;
500 level_increment
= 10 * level_increment
+ *ptr
++ - '0';
501 } while (IsDigit(*ptr
));
502 oplevel
+= level_increment
;
504 else /* I don't recognize that flag */
505 break; /* so stop processing */
510 if (!(acptr
= findNUser(nick
)) || cli_from(acptr
) != cptr
)
511 continue; /* ignore this client */
513 /* Build nick buffer */
514 nickstr
[nickpos
] = nickpos
? ',' : ' '; /* first char */
517 for (ptr
= nick
; *ptr
; ptr
++) /* store nick */
518 nickstr
[nickpos
++] = *ptr
;
520 if (current_mode
!= last_mode
) { /* if mode changed... */
521 last_mode
= current_mode
;
522 last_oplevel
= oplevel
;
524 nickstr
[nickpos
++] = ':'; /* add a specifier */
525 if (current_mode
& CHFL_VOICE
)
526 nickstr
[nickpos
++] = 'v';
527 if (current_mode
& CHFL_CHANOP
)
529 if (chptr
->mode
.apass
[0])
530 nickpos
+= ircd_snprintf(0, nickstr
+ nickpos
, sizeof(nickstr
) - nickpos
, "%u", oplevel
);
532 nickstr
[nickpos
++] = 'o';
534 } else if (current_mode
& CHFL_CHANOP
&& oplevel
!= last_oplevel
) { /* if just op level changed... */
535 nickstr
[nickpos
++] = ':'; /* add a specifier */
536 nickpos
+= ircd_snprintf(0, nickstr
+ nickpos
, sizeof(nickstr
) - nickpos
, "%u", oplevel
- last_oplevel
);
537 last_oplevel
= oplevel
;
540 if (IsBurst(sptr
) || !(member
= find_member_link(chptr
, acptr
)))
542 add_user_to_channel(chptr
, acptr
, current_mode
, oplevel
);
543 if (!(current_mode
& CHFL_DELAYED
))
544 sendcmdto_channel_butserv_butone(acptr
, CMD_JOIN
, chptr
, NULL
, 0, "%H", chptr
);
548 /* The member was already joined (either by CREATE or JOIN).
549 Remember the current mode. */
550 if (member
->status
& CHFL_CHANOP
)
551 member
->status
|= CHFL_BURST_ALREADY_OPPED
;
552 if (member
->status
& CHFL_VOICE
)
553 member
->status
|= CHFL_BURST_ALREADY_VOICED
;
554 /* Synchronize with the burst. */
555 member
->status
|= CHFL_BURST_JOINED
| (current_mode
& (CHFL_CHANOP
|CHFL_VOICE
));
556 SetOpLevel(member
, oplevel
);
562 } /* switch (*parv[param]) */
563 } /* while (param < parc) */
565 nickstr
[nickpos
] = '\0';
566 banstr
[banpos
] = '\0';
568 if (parse_flags
& MODE_PARSE_SET
) {
569 modebuf_extract(mbuf
, modestr
+ 1); /* for sending BURST onward */
570 modestr
[0] = modestr
[1] ? ' ' : '\0';
574 sendcmdto_serv_butone(sptr
, CMD_BURST
, cptr
, "%H %Tu%s%s%s", chptr
,
575 chptr
->creationtime
, modestr
, nickstr
, banstr
);
577 if (parse_flags
& MODE_PARSE_WIPEOUT
|| banpos
)
578 mode_ban_invalidate(chptr
);
580 if (parse_flags
& MODE_PARSE_SET
) { /* any modes changed? */
581 /* first deal with channel members */
582 for (member
= chptr
->members
; member
; member
= member
->next_member
) {
583 if (member
->status
& CHFL_BURST_JOINED
) { /* joined during burst */
584 if ((member
->status
& CHFL_CHANOP
) && !(member
->status
& CHFL_BURST_ALREADY_OPPED
))
585 modebuf_mode_client(mbuf
, MODE_ADD
| CHFL_CHANOP
, member
->user
, OpLevel(member
));
586 if ((member
->status
& CHFL_VOICE
) && !(member
->status
& CHFL_BURST_ALREADY_VOICED
))
587 modebuf_mode_client(mbuf
, MODE_ADD
| CHFL_VOICE
, member
->user
, OpLevel(member
));
588 } else if (parse_flags
& MODE_PARSE_WIPEOUT
) { /* wipeout old ops */
589 if (member
->status
& CHFL_CHANOP
)
590 modebuf_mode_client(mbuf
, MODE_DEL
| CHFL_CHANOP
, member
->user
, OpLevel(member
));
591 if (member
->status
& CHFL_VOICE
)
592 modebuf_mode_client(mbuf
, MODE_DEL
| CHFL_VOICE
, member
->user
, OpLevel(member
));
593 member
->status
= (member
->status
594 & ~(CHFL_CHANNEL_MANAGER
| CHFL_CHANOP
| CHFL_VOICE
))
599 /* Now deal with channel bans */
600 lp_p
= &chptr
->banlist
;
604 /* remove ban from channel */
605 if (lp
->flags
& (BAN_OVERLAPPED
| BAN_BURST_WIPEOUT
)) {
607 DupString(bandup
, lp
->banstr
);
608 modebuf_mode_string(mbuf
, MODE_DEL
| MODE_BAN
,
610 *lp_p
= lp
->next
; /* clip out of list */
613 } else if (lp
->flags
& BAN_BURSTED
) /* add ban to channel */
614 modebuf_mode_string(mbuf
, MODE_ADD
| MODE_BAN
,
615 lp
->banstr
, 0); /* don't free banstr */
617 lp
->flags
&= BAN_IPMASK
; /* reset the flag */
618 lp_p
= &(*lp_p
)->next
;
622 return mbuf
? modebuf_flush(mbuf
) : 0;