/*
* IRC - Internet Relay Chat, ircd/m_burst.c
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Computing Center
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: m_burst.c 1576 2006-05-22 06:44:50Z r33d $
*/
/*
* m_functions execute protocol messages on this server:
*
* cptr is always NON-NULL, pointing to a *LOCAL* client
* structure (with an open socket connected!). This
* identifies the physical socket where the message
* originated (or which caused the m_function to be
* executed--some m_functions may call others...).
*
* sptr is the source of the message, defined by the
* prefix part of the message if present. If not
* or prefix not found, then sptr==cptr.
*
* (!IsServer(cptr)) => (cptr == sptr), because
* prefixes are taken *only* from servers...
*
* (IsServer(cptr))
* (sptr == cptr) => the message didn't
* have the prefix.
*
* (sptr != cptr && IsServer(sptr) means
* the prefix specified servername. (?)
*
* (sptr != cptr && !IsServer(sptr) means
* that message originated from a remote
* user (not local).
*
* combining
*
* (!IsServer(sptr)) means that, sptr can safely
* taken as defining the target structure of the
* message in this server.
*
* *Always* true (if 'parse' and others are working correct):
*
* 1) sptr->from == cptr (note: cptr->from == cptr)
*
* 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
* *cannot* be a local connection, unless it's
* actually cptr!). [MyConnect(x) should probably
* be defined as (x == x->from) --msa ]
*
* parc number of variable parameter strings (if zero,
* parv is allowed to be NULL)
*
* parv a NULL terminated list of parameter pointers,
*
* parv[0], sender (prefix string), if not present
* this points to an empty string.
* parv[1]...parv[parc-1]
* pointers to additional parameters
* parv[parc] == NULL, *always*
*
* note: it is guaranteed that parv[0]..parv[parc-1] are all
* non-NULL pointers.
*/
#include "config.h"
#include "channel.h"
#include "client.h"
#include "hash.h"
#include "ircd.h"
#include "ircd_alloc.h"
#include "ircd_features.h"
#include "ircd_reply.h"
#include "ircd_string.h"
#include "list.h"
#include "match.h"
#include "msg.h"
#include "numeric.h"
#include "numnicks.h"
#include "s_conf.h"
#include "s_debug.h"
#include "s_misc.h"
#include "send.h"
#include "ircd_struct.h"
#include "support.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
static int
netride_modes(int parc, char **parv, const char *curr_key)
{
char *modes = parv[0];
int result = 0;
assert(modes && modes[0] == '+');
while (*modes) {
switch (*modes++) {
case 'i':
result |= MODE_INVITEONLY;
break;
case 'k':
if (strcmp(curr_key, *++parv))
result |= MODE_KEY;
break;
case 'l':
++parv;
break;
case 'r':
result |= MODE_REGONLY;
break;
case 'a':
result |= MODE_ADMINONLY;
break;
case 'O':
result |= MODE_OPERONLY;
break;
case 'Z':
result |= MODE_SSLONLY;
break;
}
}
return result;
}
/*
* ms_burst - server message handler
*
* -- by Run carlo@runaway.xs4all.nl december 1995 till march 1997
*
* parv[0] = sender prefix
* parv[1] = channel name
* parv[2] = channel timestamp
* The meaning of the following parv[]'s depend on their first character:
* If parv[n] starts with a '+':
* Net burst, additive modes
* parv[n] = <mode>
* parv[n+1] = <param> (optional)
* parv[n+2] = <param> (optional)
* If parv[n] starts with a '%', then n will be parc-1:
* parv[n] = %<ban> <ban> <ban> ...
* If parv[n] starts with another character:
* parv[n] = <nick>[:<mode>],<nick>[:<mode>],...
* where <mode> is the channel mode (ov) of nick and all following nicks.
*
* Example:
* "S BURST #channel 87654321 +ntkl key 123 AAA,AAB:o,BAA,BAB:ov :%ban1 ban2"
*
* Anti net.ride code.
*
* When the channel already exist, and its TS is larger then
* the TS in the BURST message, then we cancel all existing modes.
* If its is smaller then the received BURST message is ignored.
* If it's equal, then the received modes are just added.
*/
int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
struct ModeBuf modebuf, *mbuf = 0;
struct Channel *chptr;
time_t timestamp;
struct Membership *member, *nmember;
struct SLink *lp, **lp_p;
unsigned int parse_flags = (MODE_PARSE_FORCE | MODE_PARSE_BURST);
int param, nickpos = 0, banpos = 0, exceptpos = 0, type = 0, ecount = 0;
char modestr[BUFSIZE], nickstr[BUFSIZE], banstr[BUFSIZE], exceptstr[BUFSIZE];
if (parc < 3)
return protocol_violation(sptr,"Too few parameters for BURST");
if (!IsBurst(sptr)) /* don't allow BURST outside of burst */
return exit_client_msg(cptr, cptr, &me, "HACK: BURST message outside "
"net.burst from %s", cli_name(sptr));
if (!(chptr = get_channel(sptr, parv[1], CGT_CREATE)))
return 0; /* can't create the channel? */
timestamp = atoi(parv[2]);
if (!chptr->creationtime || chptr->creationtime > timestamp) {
/*
* Kick local members if channel is +i or +k and our TS was larger
* than the burst TS (anti net.ride). The modes hack is here because
* we have to do this before mode_parse, as chptr may go away.
*/
for (param = 3; param < parc; param++) {
int check_modes;
if (parv[param][0] != '+')
continue;
check_modes = netride_modes(parc - param, parv + param, chptr->mode.key);
if (check_modes) {
/* Clear any outstanding rogue invites */
mode_invite_clear(chptr);
for (member = chptr->members; member; member = nmember) {
nmember=member->next_member;
if (!MyUser(member->user) || IsZombie(member))
continue;
/* Kick as netrider if key mismatch *or* remote channel is
* +i (unless user is an oper) *or* remote channel is +r
* (unless user has an account) *or& remote channel is +z
* (unless user is using SSL) *or* remote channel is +O
* (unless user is an oper).
*/
if (!(check_modes & MODE_KEY)
&& (!(check_modes & MODE_INVITEONLY) || IsAnOper(member->user))
&& (!(check_modes & MODE_REGONLY) || IsAccount(member->user))
&& (!(check_modes & MODE_SSLONLY) || IsSSL(member->user))
&& (!(check_modes & MODE_OPERONLY) || IsAnOper(member->user))
&& (!(check_modes & MODE_ADMINONLY) || IsAdmin(member->user)))
continue;
sendcmdto_serv_butone(&his, CMD_KICK, NULL, "%H %C :Net Rider", chptr, member->user);
sendcmdto_channel_butserv_butone(&me, CMD_KICK, chptr, NULL, 0, "%H %C :Net Rider", chptr, member->user);
make_zombie(member, member->user, &me, &me, chptr);
}
}
break;
}
/* If the channel had only locals, it went away by now. */
if (!(chptr = get_channel(sptr, parv[1], CGT_CREATE)))
return 0; /* can't create the channel? */
}
/* turn off burst joined flag */
for (member = chptr->members; member; member = member->next_member)
member->status &= ~CHFL_BURST_JOINED;
if (!chptr->creationtime) /* mark channel as created during BURST */
chptr->mode.mode |= MODE_BURSTADDED;
/* new channel or an older one */
if (!chptr->creationtime || chptr->creationtime > timestamp) {
chptr->creationtime = timestamp;
modebuf_init(mbuf = &modebuf, &me, cptr, chptr,
MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY);
modebuf_mode(mbuf, MODE_DEL | chptr->mode.mode); /* wipeout modes */
chptr->mode.mode &= MODE_BURSTADDED;
parse_flags |= (MODE_PARSE_SET | MODE_PARSE_WIPEOUT); /* wipeout keys */
/* mark bans for wipeout */
for (lp = chptr->banlist; lp; lp = lp->next)
lp->flags |= CHFL_BURST_BAN_WIPEOUT;
/* mark excepts for wipeout */
for (lp = chptr->banlist; lp; lp = lp->next)
lp->flags |= CHFL_BURST_EXCEPT_WIPEOUT;
/* clear topic set by netrider (if set) */
if (*chptr->topic) {
*chptr->topic = '\0';
*chptr->topic_nick = '\0';
chptr->topic_time = 0;
sendcmdto_channel_butserv_butone(&his, CMD_TOPIC, chptr, NULL, 0,
"%H :%s", chptr, chptr->topic);
}
} else if (chptr->creationtime == timestamp) {
modebuf_init(mbuf = &modebuf, &me, cptr, chptr,
MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY);
parse_flags |= MODE_PARSE_SET; /* set new modes */
}
param = 3; /* parse parameters */
while (param < parc) {
switch (*parv[param]) {
case '+': /* parameter introduces a mode string */
param += mode_parse(mbuf, cptr, sptr, chptr, parc - param,
parv + param, parse_flags, NULL);
break;
case '%': /* parameter contains bans */
if (parse_flags & MODE_PARSE_SET) {
char *banlist = parv[param] + 1, *p = 0, *ban, *ptr;
struct SLink *newban;
for (ban = ircd_strtok(&p, banlist, " "); ban;
ban = ircd_strtok(&p, 0, " ")) {
if ((0 == ircd_strcmp("~", ban)) && (type == 0))
type = 1;
if (type == 0) {
ban = collapse(pretty_mask(ban));
/*
* Yeah, we should probably do this elsewhere, and make it better
* and more general; this will hold until we get there, though.
* I dislike the current add_banid API... -Kev
*
* I wish there were a better algo. for this than the n^2 one
* shown below *sigh*
*/
for (lp = chptr->banlist; lp; lp = lp->next) {
if (!ircd_strcmp(lp->value.ban.banstr, ban)) {
ban = 0; /* don't add ban */
lp->flags &= ~CHFL_BURST_BAN_WIPEOUT; /* not wiping out */
break; /* new ban already existed; don't even repropagate */
} else if (!(lp->flags & CHFL_BURST_BAN_WIPEOUT) &&
!mmatch(lp->value.ban.banstr, ban)) {
ban = 0; /* don't add ban unless wiping out bans */
break; /* new ban is encompassed by an existing one; drop */
} else if (!mmatch(ban, lp->value.ban.banstr))
lp->flags |= CHFL_BAN_OVERLAPPED; /* remove overlapping ban */
if (!lp->next)
break;
}
if (ban) { /* add the new ban to the end of the list */
/* Build ban buffer */
if (!banpos) {
banstr[banpos++] = ' ';
banstr[banpos++] = ':';
banstr[banpos++] = '%';
} else
banstr[banpos++] = ' ';
for (ptr = ban; *ptr; ptr++) /* add ban to buffer */
banstr[banpos++] = *ptr;
newban = make_link(); /* create new ban */
DupString(newban->value.ban.banstr, ban);
DupString(newban->value.ban.who,
cli_name(feature_bool(FEAT_HIS_BANWHO) ? &me : sptr));
newban->value.ban.when = TStime();
newban->flags = CHFL_BAN | CHFL_BURST_BAN; /* set flags */
if ((ptr = strrchr(ban, '@')) && check_if_ipmask(ptr + 1))
newban->flags |= CHFL_BAN_IPMASK;
newban->next = 0;
if (lp)
lp->next = newban; /* link it in */
else
chptr->banlist = newban;
}
} else if (type == 1) {
char *except, *ptr;
struct SLink *newexcept;
ecount++;
if (ecount > 1) { /* miss the first one as its just the ~ seperator */
except = collapse(pretty_mask(ban));
for (lp = chptr->exceptlist; lp; lp = lp->next) {
if (!ircd_strcmp(lp->value.except.exceptstr, except)) {
except = 0; /* don't add except */
lp->flags &= ~CHFL_BURST_EXCEPT_WIPEOUT; /* not wiping out */
break; /* new except already existed; don't even repropagate */
} else if (!(lp->flags & CHFL_BURST_EXCEPT_WIPEOUT) &&
!mmatch(lp->value.except.exceptstr, except)) {
except = 0; /* don't add except unless wiping out excepts */
break; /* new except is encompassed by an existing one; drop */
} else if (!mmatch(except, lp->value.except.exceptstr))
lp->flags |= CHFL_EXCEPT_OVERLAPPED; /* remove overlapping except */
if (!lp->next)
break;
}
if (except) { /* add the new except to the end of the list */
/* Build except buffer */
if (!exceptpos) {
if (!banpos) {
exceptstr[exceptpos++] = ' ';
exceptstr[exceptpos++] = ':';
exceptstr[exceptpos++] = '%';
}
exceptstr[exceptpos++] = ' ';
exceptstr[exceptpos++] = '~';
}
exceptstr[exceptpos++] = ' ';
for (ptr = except; *ptr; ptr++) /* add except to buffer */
exceptstr[exceptpos++] = *ptr;
newexcept = make_link(); /* create new except */
DupString(newexcept->value.except.exceptstr, except);
DupString(newexcept->value.except.who,
cli_name(feature_bool(FEAT_HIS_EXCEPTWHO) ? &me : sptr));
newexcept->value.except.when = TStime();
newexcept->flags = CHFL_EXCEPT | CHFL_BURST_EXCEPT; /* set flags */
if ((ptr = strrchr(except, '@')) && check_if_ipmask(ptr + 1))
newexcept->flags |= CHFL_EXCEPT_IPMASK;
newexcept->next = 0;
if (lp)
lp->next = newexcept; /* link it in */
else
chptr->exceptlist = newexcept;
}
}
}
}
}
param++; /* look at next param */
break;
default: /* parameter contains clients */
{
struct Client *acptr;
char *nicklist = parv[param], *p = 0, *nick, *ptr;
int default_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
int last_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
for (nick = ircd_strtok(&p, nicklist, ","); nick;
nick = ircd_strtok(&p, 0, ",")) {
if ((ptr = strchr(nick, ':'))) { /* new flags; deal */
*ptr++ = '\0';
if (parse_flags & MODE_PARSE_SET) {
for (default_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; *ptr;
ptr++) {
if (*ptr == 'o') /* has oper status */
default_mode = (default_mode & ~CHFL_DEOPPED) | CHFL_CHANOP;
else if (*ptr == 'h') /* halfop status */
default_mode |= CHFL_HALFOP;
else if (*ptr == 'v') /* has voice status */
default_mode |= CHFL_VOICE;
else /* I don't recognize that flag */
break; /* so stop processing */
}
}
}
if (!(acptr = findNUser(nick)) || cli_from(acptr) != cptr)
continue; /* ignore this client */
/* Build nick buffer */
nickstr[nickpos] = nickpos ? ',' : ' '; /* first char */
nickpos++;
for (ptr = nick; *ptr; ptr++) /* store nick */
nickstr[nickpos++] = *ptr;
if (default_mode != last_mode) { /* if mode changed... */
last_mode = default_mode;
nickstr[nickpos++] = ':'; /* add a specifier */
if (default_mode & CHFL_CHANOP)
nickstr[nickpos++] = 'o';
if (default_mode & CHFL_HALFOP)
nickstr[nickpos++] = 'h';
if (default_mode & CHFL_VOICE)
nickstr[nickpos++] = 'v';
}
add_user_to_channel(chptr, acptr, default_mode);
sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, 0, "%H", chptr);
}
}
param++;
break;
} /* switch (*parv[param]) { */
} /* while (param < parc) { */
nickstr[nickpos] = '\0';
banstr[banpos] = '\0';
exceptstr[exceptpos] = '\0';
if (parse_flags & MODE_PARSE_SET) {
modebuf_extract(mbuf, modestr + 1); /* for sending BURST onward */
modestr[0] = modestr[1] ? ' ' : '\0';
} else
modestr[0] = '\0';
sendcmdto_serv_butone(sptr, CMD_BURST, cptr, "%H %Tu%s%s%s%s", chptr,
chptr->creationtime, modestr, nickstr, banstr,
feature_bool(FEAT_EXCEPTS) ? exceptstr : "");
if (parse_flags & MODE_PARSE_WIPEOUT || banpos)
mode_ban_invalidate(chptr);
if (parse_flags & MODE_PARSE_WIPEOUT || exceptpos)
mode_except_invalidate(chptr);
if (parse_flags & MODE_PARSE_SET) { /* any modes changed? */
/* first deal with channel members */
for (member = chptr->members; member; member = member->next_member) {
if (member->status & CHFL_BURST_JOINED) { /* joined during burst */
if (member->status & CHFL_CHANOP)
modebuf_mode_client(mbuf, MODE_ADD | CHFL_CHANOP, member->user);
if (member->status & CHFL_HALFOP)
modebuf_mode_client(mbuf, MODE_ADD | CHFL_HALFOP, member->user);
if (member->status & CHFL_VOICE)
modebuf_mode_client(mbuf, MODE_ADD | CHFL_VOICE, member->user);
} else if (parse_flags & MODE_PARSE_WIPEOUT) { /* wipeout old ops */
if (member->status & CHFL_CHANOP)
modebuf_mode_client(mbuf, MODE_DEL | CHFL_CHANOP, member->user);
if (member->status & CHFL_HALFOP)
modebuf_mode_client(mbuf, MODE_DEL | CHFL_HALFOP, member->user);
if (member->status & CHFL_VOICE)
modebuf_mode_client(mbuf, MODE_DEL | CHFL_VOICE, member->user);
member->status = ((member->status & ~(CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE)) |
CHFL_DEOPPED);
}
}
/* Now deal with channel bans */
lp_p = &chptr->banlist;
while (*lp_p) {
lp = *lp_p;
/* remove ban from channel */
if (lp->flags & (CHFL_BAN_OVERLAPPED | CHFL_BURST_BAN_WIPEOUT)) {
modebuf_mode_string(mbuf, MODE_DEL | MODE_BAN,
lp->value.ban.banstr, 1); /* let it free banstr */
*lp_p = lp->next; /* clip out of list */
MyFree(lp->value.ban.who); /* free who */
free_link(lp); /* free ban */
continue;
} else if (lp->flags & CHFL_BURST_BAN) /* add ban to channel */
modebuf_mode_string(mbuf, MODE_ADD | MODE_BAN,
lp->value.ban.banstr, 0); /* don't free banstr */
lp->flags &= (CHFL_BAN | CHFL_BAN_IPMASK); /* reset the flag */
lp_p = &(*lp_p)->next;
}
/* Now deal with channel excepts */
lp_p = &chptr->exceptlist;
while (*lp_p) {
lp = *lp_p;
/* remove except from channel */
if (lp->flags & (CHFL_EXCEPT_OVERLAPPED | CHFL_BURST_EXCEPT_WIPEOUT)) {
modebuf_mode_string(mbuf, MODE_DEL | MODE_EXCEPT,
lp->value.except.exceptstr, 1); /* let it free exceptstr */
*lp_p = lp->next; /* clip out of list */
MyFree(lp->value.except.who); /* free who */
free_link(lp); /* free except */
continue;
} else if (lp->flags & CHFL_BURST_EXCEPT) /* add except to channel */
modebuf_mode_string(mbuf, MODE_ADD | MODE_EXCEPT,
lp->value.except.exceptstr, 0); /* don't free exceptstr */
lp->flags &= (CHFL_EXCEPT | CHFL_EXCEPT_IPMASK); /* reset the flag */
lp_p = &(*lp_p)->next;
}
}
return mbuf ? modebuf_flush(mbuf) : 0;
}
syntax highlighted by Code2HTML, v. 0.9.1