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 2005/09/27 02:41:57 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
|| chptr
->creationtime
> timestamp
) {
224 * Kick local members if channel is +i or +k and our TS was larger
225 * than the burst TS (anti net.ride). The modes hack is here because
226 * we have to do this before mode_parse, as chptr may go away.
228 for (param
= 3; param
< parc
; param
++)
231 if (parv
[param
][0] != '+')
233 check_modes
= netride_modes(parc
- param
, parv
+ param
, chptr
->mode
.key
);
236 /* Clear any outstanding rogue invites */
237 mode_invite_clear(chptr
);
238 for (member
= chptr
->members
; member
; member
= nmember
)
240 nmember
= member
->next_member
;
241 if (!MyUser(member
->user
) || IsZombie(member
))
243 /* Kick as netrider if key mismatch *or* remote channel is
244 * +i (unless user is an oper) *or* remote channel is +r
245 * (unless user has an account).
247 if (!(check_modes
& MODE_KEY
)
248 && (!(check_modes
& MODE_INVITEONLY
) || IsAnOper(member
->user
))
249 && (!(check_modes
& MODE_REGONLY
) || IsAccount(member
->user
)))
251 sendcmdto_serv_butone(&me
, CMD_KICK
, NULL
, "%H %C :Net Rider", chptr
, member
->user
);
252 sendcmdto_channel_butserv_butone(&his
, CMD_KICK
, chptr
, NULL
, 0, "%H %C :Net Rider", chptr
, member
->user
);
253 make_zombie(member
, member
->user
, &me
, &me
, chptr
);
259 /* If the channel had only locals, it went away by now. */
260 if (!(chptr
= get_channel(sptr
, parv
[1], CGT_CREATE
)))
261 return 0; /* can't create the channel? */
264 /* turn off burst joined flag */
265 for (member
= chptr
->members
; member
; member
= member
->next_member
)
266 member
->status
&= ~(CHFL_BURST_JOINED
|CHFL_BURST_ALREADY_OPPED
|CHFL_BURST_ALREADY_VOICED
);
268 if (!chptr
->creationtime
) /* mark channel as created during BURST */
269 chptr
->mode
.mode
|= MODE_BURSTADDED
;
271 /* new channel or an older one */
272 if (!chptr
->creationtime
|| chptr
->creationtime
> timestamp
) {
273 chptr
->creationtime
= timestamp
;
275 modebuf_init(mbuf
= &modebuf
, &me
, cptr
, chptr
,
276 MODEBUF_DEST_CHANNEL
| MODEBUF_DEST_NOKEY
);
277 modebuf_mode(mbuf
, MODE_DEL
| chptr
->mode
.mode
); /* wipeout modes */
278 chptr
->mode
.mode
&= MODE_BURSTADDED
| MODE_WASDELJOINS
;
280 /* wipe out modes not represented in chptr->mode.mode */
281 if (chptr
->mode
.limit
) {
282 modebuf_mode_uint(mbuf
, MODE_DEL
| MODE_LIMIT
, chptr
->mode
.limit
);
283 chptr
->mode
.limit
= 0;
285 if (chptr
->mode
.key
[0]) {
286 modebuf_mode_string(mbuf
, MODE_DEL
| MODE_KEY
, chptr
->mode
.key
, 0);
287 chptr
->mode
.key
[0] = '\0';
289 if (chptr
->mode
.upass
[0]) {
290 modebuf_mode_string(mbuf
, MODE_DEL
| MODE_UPASS
, chptr
->mode
.upass
, 0);
291 chptr
->mode
.upass
[0] = '\0';
293 if (chptr
->mode
.apass
[0]) {
294 modebuf_mode_string(mbuf
, MODE_DEL
| MODE_APASS
, chptr
->mode
.apass
, 0);
295 chptr
->mode
.apass
[0] = '\0';
298 parse_flags
|= (MODE_PARSE_SET
| MODE_PARSE_WIPEOUT
); /* wipeout keys */
300 /* mark bans for wipeout */
301 for (lp
= chptr
->banlist
; lp
; lp
= lp
->next
)
302 lp
->flags
|= BAN_BURST_WIPEOUT
;
304 /* clear topic set by netrider (if set) */
306 *chptr
->topic
= '\0';
307 *chptr
->topic_nick
= '\0';
308 chptr
->topic_time
= 0;
309 sendcmdto_channel_butserv_butone(&his
, CMD_TOPIC
, chptr
, NULL
, 0,
310 "%H :%s", chptr
, chptr
->topic
);
312 } else if (chptr
->creationtime
== timestamp
) {
313 modebuf_init(mbuf
= &modebuf
, &me
, cptr
, chptr
,
314 MODEBUF_DEST_CHANNEL
| MODEBUF_DEST_NOKEY
);
316 parse_flags
|= MODE_PARSE_SET
; /* set new modes */
319 param
= 3; /* parse parameters */
320 while (param
< parc
) {
321 switch (*parv
[param
]) {
322 case '+': /* parameter introduces a mode string */
323 param
+= mode_parse(mbuf
, cptr
, sptr
, chptr
, parc
- param
,
324 parv
+ param
, parse_flags
, NULL
);
327 case '%': /* parameter contains bans */
328 if (parse_flags
& MODE_PARSE_SET
) {
329 char *banlist
= parv
[param
] + 1, *p
= 0, *ban
, *ptr
;
332 for (ban
= ircd_strtok(&p
, banlist
, " "); ban
;
333 ban
= ircd_strtok(&p
, 0, " ")) {
334 ban
= collapse(pretty_mask(ban
));
337 * Yeah, we should probably do this elsewhere, and make it better
338 * and more general; this will hold until we get there, though.
339 * I dislike the current add_banid API... -Kev
341 * I wish there were a better algo. for this than the n^2 one
344 for (lp
= chptr
->banlist
; lp
; lp
= lp
->next
) {
345 if (!ircd_strcmp(lp
->banstr
, ban
)) {
346 ban
= 0; /* don't add ban */
347 lp
->flags
&= ~BAN_BURST_WIPEOUT
; /* not wiping out */
348 break; /* new ban already existed; don't even repropagate */
349 } else if (!(lp
->flags
& BAN_BURST_WIPEOUT
) &&
350 !mmatch(lp
->banstr
, ban
)) {
351 ban
= 0; /* don't add ban unless wiping out bans */
352 break; /* new ban is encompassed by an existing one; drop */
353 } else if (!mmatch(ban
, lp
->banstr
))
354 lp
->flags
|= BAN_OVERLAPPED
; /* remove overlapping ban */
360 if (ban
) { /* add the new ban to the end of the list */
361 /* Build ban buffer */
363 banstr
[banpos
++] = ' ';
364 banstr
[banpos
++] = ':';
365 banstr
[banpos
++] = '%';
367 banstr
[banpos
++] = ' ';
368 for (ptr
= ban
; *ptr
; ptr
++) /* add ban to buffer */
369 banstr
[banpos
++] = *ptr
;
371 newban
= make_ban(ban
); /* create new ban */
372 strcpy(newban
->who
, "*");
373 newban
->when
= TStime();
374 newban
->flags
|= BAN_BURSTED
;
377 lp
->next
= newban
; /* link it in */
379 chptr
->banlist
= newban
;
383 param
++; /* look at next param */
386 default: /* parameter contains clients */
388 struct Client
*acptr
;
389 char *nicklist
= parv
[param
], *p
= 0, *nick
, *ptr
;
390 int current_mode
, last_mode
, base_mode
;
391 int oplevel
= -1; /* Mark first field with digits: means the same as 'o' (but with level). */
392 int last_oplevel
= 0;
393 struct Membership
* member
;
395 base_mode
= CHFL_DEOPPED
| CHFL_BURST_JOINED
;
396 if (chptr
->mode
.mode
& MODE_DELJOINS
)
397 base_mode
|= CHFL_DELAYED
;
398 current_mode
= last_mode
= base_mode
;
400 for (nick
= ircd_strtok(&p
, nicklist
, ","); nick
;
401 nick
= ircd_strtok(&p
, 0, ",")) {
403 if ((ptr
= strchr(nick
, ':'))) { /* new flags; deal */
406 if (parse_flags
& MODE_PARSE_SET
) {
407 int current_mode_needs_reset
;
408 for (current_mode_needs_reset
= 1; *ptr
; ptr
++) {
409 if (*ptr
== 'o') { /* has oper status */
411 * An 'o' is pre-oplevel protocol, so this is only for
412 * backwards compatibility. Give them an op-level of
413 * MAXOPLEVEL so everyone can deop them.
415 oplevel
= MAXOPLEVEL
;
416 if (current_mode_needs_reset
) {
417 current_mode
= base_mode
;
418 current_mode_needs_reset
= 0;
420 current_mode
= (current_mode
& ~(CHFL_DEOPPED
| CHFL_DELAYED
)) | CHFL_CHANOP
;
422 else if (*ptr
== 'v') { /* has voice status */
423 if (current_mode_needs_reset
) {
424 current_mode
= base_mode
;
425 current_mode_needs_reset
= 0;
427 current_mode
= (current_mode
& ~CHFL_DELAYED
) | CHFL_VOICE
;
428 oplevel
= -1; /* subsequent digits are an absolute op-level value. */
430 else if (isdigit(*ptr
)) {
431 int level_increment
= 0;
432 if (oplevel
== -1) { /* op-level is absolute value? */
433 if (current_mode_needs_reset
) {
434 current_mode
= base_mode
;
435 current_mode_needs_reset
= 0;
439 current_mode
= (current_mode
& ~(CHFL_DEOPPED
| CHFL_DELAYED
)) | CHFL_CHANOP
;
441 level_increment
= 10 * level_increment
+ *ptr
++ - '0';
442 } while(isdigit(*ptr
));
443 oplevel
+= level_increment
;
445 else /* I don't recognize that flag */
446 break; /* so stop processing */
451 if (!(acptr
= findNUser(nick
)) || cli_from(acptr
) != cptr
)
452 continue; /* ignore this client */
454 /* Build nick buffer */
455 nickstr
[nickpos
] = nickpos
? ',' : ' '; /* first char */
458 for (ptr
= nick
; *ptr
; ptr
++) /* store nick */
459 nickstr
[nickpos
++] = *ptr
;
461 if (current_mode
!= last_mode
) { /* if mode changed... */
462 last_mode
= current_mode
;
463 last_oplevel
= oplevel
;
465 nickstr
[nickpos
++] = ':'; /* add a specifier */
466 if (current_mode
& CHFL_VOICE
)
467 nickstr
[nickpos
++] = 'v';
468 if (current_mode
& CHFL_CHANOP
)
470 if (chptr
->mode
.apass
[0])
471 nickpos
+= ircd_snprintf(0, nickstr
+ nickpos
, sizeof(nickstr
) - nickpos
, "%u", oplevel
);
473 nickstr
[nickpos
++] = 'o';
475 } else if (current_mode
& CHFL_CHANOP
&& oplevel
!= last_oplevel
) { /* if just op level changed... */
476 nickstr
[nickpos
++] = ':'; /* add a specifier */
477 nickpos
+= ircd_snprintf(0, nickstr
+ nickpos
, sizeof(nickstr
) - nickpos
, "%u", oplevel
- last_oplevel
);
478 last_oplevel
= oplevel
;
481 if (IsBurst(sptr
) || !(member
= find_member_link(chptr
, acptr
)))
483 add_user_to_channel(chptr
, acptr
, current_mode
, oplevel
);
484 if (!(current_mode
& CHFL_DELAYED
))
485 sendcmdto_channel_butserv_butone(acptr
, CMD_JOIN
, chptr
, NULL
, 0, "%H", chptr
);
489 /* The member was already joined (either by CREATE or JOIN).
490 Remember the current mode. */
491 if (member
->status
& CHFL_CHANOP
)
492 member
->status
|= CHFL_BURST_ALREADY_OPPED
;
493 if (member
->status
& CHFL_VOICE
)
494 member
->status
|= CHFL_BURST_ALREADY_VOICED
;
495 /* Synchronize with the burst. */
496 member
->status
|= CHFL_BURST_JOINED
| (current_mode
& (CHFL_CHANOP
|CHFL_VOICE
));
497 SetOpLevel(member
, oplevel
);
503 } /* switch (*parv[param]) */
504 } /* while (param < parc) */
506 nickstr
[nickpos
] = '\0';
507 banstr
[banpos
] = '\0';
509 if (parse_flags
& MODE_PARSE_SET
) {
510 modebuf_extract(mbuf
, modestr
+ 1); /* for sending BURST onward */
511 modestr
[0] = modestr
[1] ? ' ' : '\0';
515 sendcmdto_serv_butone(sptr
, CMD_BURST
, cptr
, "%H %Tu%s%s%s", chptr
,
516 chptr
->creationtime
, modestr
, nickstr
, banstr
);
518 if (parse_flags
& MODE_PARSE_WIPEOUT
|| banpos
)
519 mode_ban_invalidate(chptr
);
521 if (parse_flags
& MODE_PARSE_SET
) { /* any modes changed? */
522 /* first deal with channel members */
523 for (member
= chptr
->members
; member
; member
= member
->next_member
) {
524 if (member
->status
& CHFL_BURST_JOINED
) { /* joined during burst */
525 if ((member
->status
& CHFL_CHANOP
) && !(member
->status
& CHFL_BURST_ALREADY_OPPED
))
526 modebuf_mode_client(mbuf
, MODE_ADD
| CHFL_CHANOP
, member
->user
, OpLevel(member
));
527 if ((member
->status
& CHFL_VOICE
) && !(member
->status
& CHFL_BURST_ALREADY_VOICED
))
528 modebuf_mode_client(mbuf
, MODE_ADD
| CHFL_VOICE
, member
->user
, OpLevel(member
));
529 } else if (parse_flags
& MODE_PARSE_WIPEOUT
) { /* wipeout old ops */
530 if (member
->status
& CHFL_CHANOP
)
531 modebuf_mode_client(mbuf
, MODE_DEL
| CHFL_CHANOP
, member
->user
, OpLevel(member
));
532 if (member
->status
& CHFL_VOICE
)
533 modebuf_mode_client(mbuf
, MODE_DEL
| CHFL_VOICE
, member
->user
, OpLevel(member
));
534 member
->status
= (member
->status
535 & ~(CHFL_CHANNEL_MANAGER
| CHFL_CHANOP
| CHFL_VOICE
))
540 /* Now deal with channel bans */
541 lp_p
= &chptr
->banlist
;
545 /* remove ban from channel */
546 if (lp
->flags
& (BAN_OVERLAPPED
| BAN_BURST_WIPEOUT
)) {
548 DupString(bandup
, lp
->banstr
);
549 modebuf_mode_string(mbuf
, MODE_DEL
| MODE_BAN
,
551 *lp_p
= lp
->next
; /* clip out of list */
554 } else if (lp
->flags
& BAN_BURSTED
) /* add ban to channel */
555 modebuf_mode_string(mbuf
, MODE_ADD
| MODE_BAN
,
556 lp
->banstr
, 0); /* don't free banstr */
558 lp
->flags
&= BAN_IPMASK
; /* reset the flag */
559 lp_p
= &(*lp_p
)->next
;
563 return mbuf
? modebuf_flush(mbuf
) : 0;