+2002-03-13 Carlo Wood <run@alinoe.com>
+
+ * include/channel.h: CHFL_CHANNEL_MANAGER, new local
+ channel flag set when someone creates a channel or joins
+ using the Apass. IsChannelManager(), SetChannelManager():
+ macros to manipulate new channel flag.
+ channel_modes: Added new argument to avoid calling
+ find_member_link more often than needed.
+
+ * include/numeric.h: RPL_APASSWARN, ERR_NOTLOWEROPLEVEL,
+ ERR_NOTMANAGER, ERR_CHANSECURED, ERR_UPASSSET,
+ ERR_UPASSNOTSET: new numeric replies.
+
+ * ircd/channel.c: is_level0_op: removed.
+ member_can_send_to_channel: disallow channel manager
+ to talk. channel_modes: show upass to level0 ops.
+ mode_parse_upass: Only the channel manager is allowed
+ to change the upass. Only allow to set upass when apass
+ is also set. mode_parse_apass: Don't allow to change the
+ Apass if the channel is older than 48 hours. Only allow
+ to remove the apass when upass is not set. Send clear
+ warnings regarding the importance of apass.
+ mode_process_clients: Don't change the oplevel of an opped
+ member in a channel where upass is not set.
+
+ * ircd/destruct_event.c: exec_expired_destruct_events:
+ Bug fix: send DESTRUCT message when destructing a channel.
+
+ * ircd/m_destruct.c: ms_destruct: Bug fix: use self as
+ prefix for DESTRUCT message.
+
+ * ircd/m_join.c: m_join: Handle apass and upass.
+
+ * ircd/m_kick.c: m_kick: Don't allow to kick member with
+ a higher or equal op-level.
+
+ * ircd/m_mode.c: m_mode: Now pass member to channel_modes.
+ ms_mode: Allow server to do modes on channels with apass
+ set.
+
+ * ircd/s_err.c: RPL_APASSWARN, ERR_NOTLOWEROPLEVEL,
+ ERR_NOTMANAGER, ERR_CHANSECURED, ERR_UPASSSET,
+ ERR_UPASSNOTSET: new numeric replies.
+
+
2002-03-10 Joseph Bongaarts <foxxe@wtfs.net>
* ircd/m_kill.c: Last of the bug fixes for do_kill()
#define CHFL_SILENCE_IPMASK 0x2000 /* silence mask is an IP-number mask */
#define CHFL_BURST_ALREADY_OPPED 0x04000 /* In oob BURST, but was already joined and opped */
#define CHFL_BURST_ALREADY_VOICED 0x08000 /* In oob BURST, but was already joined and voiced */
+#define CHFL_CHANNEL_MANAGER 0x10000 /* Set when creating channel or using Apass */
#define CHFL_OVERLAP (CHFL_CHANOP | CHFL_VOICE)
#define CHFL_BANVALIDMASK (CHFL_BANVALID | CHFL_BANNED)
#define IsServOpOk(x) ((x)->status & CHFL_SERVOPOK)
#define IsBurstJoined(x) ((x)->status & CHFL_BURST_JOINED)
#define IsVoicedOrOpped(x) ((x)->status & CHFL_VOICED_OR_OPPED)
+#define IsChannelManager(x) ((x)->status & CHFL_CHANNEL_MANAGER)
#define SetBanned(x) ((x)->status |= CHFL_BANNED)
#define SetBanValid(x) ((x)->status |= CHFL_BANVALID)
#define SetServOpOk(x) ((x)->status |= CHFL_SERVOPOK)
#define SetBurstJoined(x) ((x)->status |= CHFL_BURST_JOINED)
#define SetZombie(x) ((x)->status |= CHFL_ZOMBIE)
+#define SetChannelManager(x) ((x)->status |= CHFL_CHANNEL_MANAGER)
#define SetOpLevel(x, v) (void)((x)->oplevel = (v))
#define ClearBanned(x) ((x)->status &= ~CHFL_BANNED)
*/
extern void clean_channelname(char* name);
extern void channel_modes(struct Client *cptr, char *mbuf, char *pbuf,
- int buflen, struct Channel *chptr);
+ int buflen, struct Channel *chptr,
+ struct Membership *member);
extern int set_mode(struct Client* cptr, struct Client* sptr,
struct Channel* chptr, int parc, char* parv[],
char* mbuf, char* pbuf, char* npbuf, int* badop);
#define RPL_MAP 15 /* Undernet extension */
#define RPL_MAPMORE 16 /* Undernet extension */
#define RPL_MAPEND 17 /* Undernet extension */
+#define RPL_APASSWARN 30 /* Undernet extension */
/* RPL_YOURID 42 IRCnet extension */
/* RPL_ATTEMPTINGJUNC 50 aircd extension */
/* RPL_ATTEMPTINGREROUTE 51 aircd extension */
ERR_WHOSYNTAX 522 dalnet
ERR_WHOLIMEXCEED 523 dalnet */
-#define ERR_NOTLOWEROPLEVEL 550
-#define ERR_LASTERROR 551
+#define ERR_NOTLOWEROPLEVEL 550 /* Undernet extension */
+#define ERR_NOTMANAGER 551 /* Undernet extension */
+#define ERR_CHANSECURED 552 /* Undernet extension */
+#define ERR_UPASSSET 553 /* Undernet extension */
+#define ERR_UPASSNOTSET 554 /* Undernet extension */
+#define ERR_LASTERROR 555
/* RPL_LOGON 600 dalnet,unreal
RPL_LOGOFF 601 dalnet,unreal
return 0;
}
-int is_level0_op(struct Client *cptr, struct Channel *chptr)
-{
- return 0;
-}
-
int is_zombie(struct Client *cptr, struct Channel *chptr)
{
struct Membership* member;
{
assert(0 != member);
+ /* Discourage using the Apass to get op. They should use the upass. */
+ if (IsChannelManager(member) && *member->channel->mode.upass)
+ return 0;
+
if (IsVoicedOrOpped(member))
return 1;
/*
* with the parameters in pbuf.
*/
void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
- struct Channel *chptr)
+ struct Channel *chptr, struct Membership *member)
{
int previous_parameter = 0;
*mbuf++ = 'u';
if (previous_parameter)
strcat(pbuf, " ");
- if (is_level0_op(cptr, chptr) || IsServer(cptr)) {
+ if (IsServer(cptr) || (member && IsChanOp(member) && OpLevel(member) == 0)) {
strcat(pbuf, chptr->mode.upass);
} else
strcat(pbuf, "*");
lp2 = chptr->banlist;
*modebuf = *parabuf = '\0';
- channel_modes(cptr, modebuf, parabuf, sizeof(parabuf), chptr);
+ channel_modes(cptr, modebuf, parabuf, sizeof(parabuf), chptr, 0);
for (first = 1; full; first = 0) /* Loop for multiple messages */
{
return;
}
+ /* If they are not the channel manager, they are not allowed to change it */
+ if (MyUser(state->sptr) && !IsChannelManager(state->member)) {
+ if (*state->chptr->mode.apass) {
+ send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
+ "Use /JOIN", state->chptr->chname, "<AdminPass>.");
+ } else {
+ send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
+ "Re-create the channel. The channel must be *empty* for",
+ TStime() - state->chptr->creationtime >= 171000 ? "48 contigious hours" : "a minute or two",
+ "before it can be recreated.");
+ }
+ return;
+ }
+
if (state->done & DONE_UPASS) /* allow upass to be set only once */
return;
state->done |= DONE_UPASS;
if (!state->mbuf)
return;
- /* can't add a upass if one is set, nor can one remove the wrong upass */
if (!(state->flags & MODE_PARSE_FORCE))
+ /* can't add the upass while apass is not set */
+ if (state->dir == MODE_ADD && !*state->chptr->mode.apass) {
+ send_reply(state->sptr, ERR_UPASSNOTSET, state->chptr->chname, state->chptr->chname);
+ return;
+ }
+ /* can't add a upass if one is set, nor can one remove the wrong upass */
if ((state->dir == MODE_ADD && *state->chptr->mode.upass) ||
(state->dir == MODE_DEL &&
ircd_strcmp(state->chptr->mode.upass, t_str))) {
return;
}
+ /* Don't allow to change the Apass if the channel is older than 48 hours. */
+ if (TStime() - state->chptr->creationtime >= 172800 && !IsAnOper(state->sptr)) {
+ send_reply(state->sptr, ERR_CHANSECURED, state->chptr->chname);
+ return;
+ }
+
+ /* If they are not the channel manager, they are not allowed to change it */
+ if (MyUser(state->sptr) && !IsChannelManager(state->member)) {
+ if (*state->chptr->mode.apass) {
+ send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
+ "Use /JOIN", state->chptr->chname, "<AdminPass>.");
+ } else {
+ send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
+ "Re-create the channel. The channel must be *empty* for",
+ "at least a whole minute", "before it can be recreated.");
+ }
+ return;
+ }
+
if (state->done & DONE_APASS) /* allow apass to be set only once */
return;
state->done |= DONE_APASS;
if (!state->mbuf)
return;
- /* can't add a apass if one is set, nor can one remove the wrong apass */
- if (!(state->flags & MODE_PARSE_FORCE))
+ if (!(state->flags & MODE_PARSE_FORCE)) {
+ /* can't remove the apass while upass is still set */
+ if (state->dir == MODE_DEL && *state->chptr->mode.upass) {
+ send_reply(state->sptr, ERR_UPASSSET, state->chptr->chname, state->chptr->chname);
+ return;
+ }
+ /* can't add an apass if one is set, nor can one remove the wrong apass */
if ((state->dir == MODE_ADD && *state->chptr->mode.apass) ||
- (state->dir == MODE_DEL &&
- ircd_strcmp(state->chptr->mode.apass, t_str))) {
+ (state->dir == MODE_DEL && ircd_strcmp(state->chptr->mode.apass, t_str))) {
send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
return;
}
+ }
if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
!ircd_strcmp(state->chptr->mode.apass, t_str))
modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
if (state->flags & MODE_PARSE_SET) {
- if (state->dir == MODE_ADD) /* set the new apass */
+ if (state->dir == MODE_ADD) { /* set the new apass */
+ /* Make it VERY clear to the user that this is a one-time password */
ircd_strncpy(state->chptr->mode.apass, t_str, PASSLEN);
- else /* remove the old apass */
+ if (MyUser(state->sptr)) {
+ send_reply(state->sptr, RPL_APASSWARN,
+ "Channel Admin password (+A) set to '", state->chptr->mode.apass, "'. ",
+ "Are you SURE you want to use this as Admin password? ",
+ "You will NOT be able to change this password anymore once the channel is more than 48 hours old!");
+ send_reply(state->sptr, RPL_APASSWARN,
+ "Use \"/MODE ", state->chptr->chname, " -A ", state->chptr->mode.apass,
+ "\" to remove the password and then immediatly set a new one. "
+ "IMPORTANT: YOU CANNOT RECOVER THIS PASSWORD, EVER; "
+ "WRITE THE PASSWORD DOWN (don't store this rescue password on disk)! "
+ "Now set the channel user password (+u).");
+ }
+ } else { /* remove the old apass */
*state->chptr->mode.apass = '\0';
+ if (MyUser(state->sptr))
+ send_reply(state->sptr, RPL_APASSWARN,
+ "WARNING: You removed the channel Admin password MODE (+A). ",
+ "If you would disconnect or leave the channel without setting a new password then you will ",
+ "not be able to set it again and lose ownership of this channel! ",
+ "SET A NEW PASSWORD NOW!", "");
+ }
}
}
/* set op-level of member being opped */
if ((state->cli_change[i].flag & (MODE_ADD | MODE_CHANOP)) ==
(MODE_ADD | MODE_CHANOP)) {
- int old_level = (state->member == NULL) ? -1 : OpLevel(state->member);
- SetOpLevel(member, old_level == MAXOPLEVEL ? MAXOPLEVEL : (old_level + 1));
+ /* If on a channel with upass set, someone with level x gives ops to someone else,
+ then that person gets level x-1. On other channels, where upass is not set,
+ the level stays the same. */
+ int level_increment = *state->chptr->mode.upass ? 1 : 0;
+ /* Someone being opped by a server gets op-level 0 */
+ int old_level = (state->member == NULL) ? -level_increment : OpLevel(state->member);
+ SetOpLevel(member, old_level == MAXOPLEVEL ? MAXOPLEVEL : (old_level + level_increment));
}
/* accumulate the change */
#include "ircd_alloc.h"
#include "ircd.h"
#include "ircd_events.h"
+#include "send.h"
+#include "msg.h"
#include <assert.h>
#include <stdlib.h>
while (*list_bottom && TStime() >= (*list_bottom)->expires)
{
struct Channel* chptr = (*list_bottom)->chptr;
+ /* Send DESTRUCT message */
+ sendcmdto_serv_butone(&me, CMD_DESTRUCT, 0, "%s %Tu", chptr->chname, chptr->creationtime);
remove_destruct_event(chptr);
destruct_channel(chptr);
}
}
/* Pass on DESTRUCT message and ALSO bounce it back! */
- sendcmdto_serv_butone(sptr, CMD_DESTRUCT, 0, "%s %Tu", parv[1], chanTS);
+ sendcmdto_serv_butone(&me, CMD_DESTRUCT, 0, "%s %Tu", parv[1], chanTS);
/* Remove the empty channel. */
remove_destruct_event(chptr);
struct Channel *chptr;
struct JoinBuf join;
struct JoinBuf create;
+ struct ModeBuf mbuf;
struct Gline *gline;
unsigned int flags = 0;
int i;
}
if (chptr) {
+ int is_level0_op = 0;
+ if (!BadPtr(keys) && *chptr->mode.apass) {
+ /* Don't use compall for the apass, only a single key is allowed.
+ Test Apass first in case someone set Apass and upass equal. */
+ if (strcmp(chptr->mode.apass, keys) == 0) {
+ is_level0_op = 1;
+ flags &= ~CHFL_DEOPPED;
+ flags |= CHFL_CHANOP | CHFL_CHANNEL_MANAGER;
+ }
+ else if (*chptr->mode.upass && strcmp(chptr->mode.upass, keys) == 0) {
+ is_level0_op = 1;
+ flags &= ~CHFL_DEOPPED;
+ flags |= CHFL_CHANOP;
+ }
+ }
if (check_target_limit(sptr, chptr, chptr->chname, 0))
continue; /* exceeded target limit */
- else if ((i = can_join(sptr, chptr, keys))) {
+ else if (!is_level0_op && (i = can_join(sptr, chptr, keys))) {
if (i > MAGIC_OPER_OVERRIDE) { /* oper overrode mode */
switch (i - MAGIC_OPER_OVERRIDE) {
case ERR_CHANNELISFULL: /* figure out which mode */
} /* else if ((i = can_join(sptr, chptr, keys))) */
joinbuf_join(&join, chptr, flags);
+ if (is_level0_op)
+ {
+ joinbuf_flush(&join);
+ modebuf_init(&mbuf, &me, cptr, chptr,
+ MODEBUF_DEST_CHANNEL | /* Send mode to channel */
+ MODEBUF_DEST_SERVER); /* And send it to the other servers */
+ modebuf_mode_client(&mbuf,
+ MODE_ADD | MODE_CHANOP, sptr); /* Give ops to the level0 op */
+ modebuf_flush(&mbuf);
+ }
} else if (!(chptr = get_channel(sptr, name, CGT_CREATE)))
continue; /* couldn't get channel */
else if (check_target_limit(sptr, chptr, chptr->chname, 1)) {
destruct_channel(chptr); /* created it... */
continue;
} else
- joinbuf_join(&create, chptr, flags);
+ joinbuf_join(&create, chptr, flags | CHFL_CHANNEL_MANAGER);
del_invite(sptr, chptr);
struct Client *who;
struct Channel *chptr;
struct Membership *member = 0;
+ struct Membership* member2;
char *name, *comment;
cli_flags(sptr) &= ~FLAGS_TS8;
if (!(chptr = get_channel(sptr, name, CGT_NO_CREATE)))
return send_reply(sptr, ERR_NOSUCHCHANNEL, name);
- if (!is_chan_op(sptr, chptr) || IsModelessChannel(name))
+ if (!(member2 = find_member_link(chptr, sptr)) || IsZombie(member2)
+ || !IsChanOp(member2) || IsModelessChannel(name))
return send_reply(sptr, ERR_CHANOPRIVSNEEDED, name);
if (!(who = find_chasing(sptr, parv[2], 0)))
if (!(member = find_member_link(chptr, who)) || IsZombie(member))
return send_reply(sptr, ERR_USERNOTINCHANNEL, cli_name(who), chptr->chname);
+ /* Don't allow to kick member with a higher or equal op-level */
+ if (OpLevel(member) <= OpLevel(member2))
+ return send_reply(sptr, ERR_NOTLOWEROPLEVEL, cli_name(who), chptr->chname,
+ OpLevel(member2), OpLevel(member), "kick",
+ OpLevel(member) == OpLevel(member2) ? "the same" : "a higher");
+
/* We rely on ircd_snprintf to truncate the comment */
comment = EmptyString(parv[parc - 1]) ? parv[0] : parv[parc - 1];
cli_flags(sptr) &= ~FLAGS_TS8;
+ member = find_member_link(chptr, sptr);
+
if (parc < 3) {
char modebuf[MODEBUFLEN];
char parabuf[MODEBUFLEN];
*modebuf = *parabuf = '\0';
modebuf[1] = '\0';
- channel_modes(sptr, modebuf, parabuf, sizeof(parabuf), chptr);
+ channel_modes(sptr, modebuf, parabuf, sizeof(parabuf), chptr, member);
send_reply(sptr, RPL_CHANNELMODEIS, chptr->chname, modebuf, parabuf);
send_reply(sptr, RPL_CREATIONTIME, chptr->chname, chptr->creationtime);
return 0;
}
- if (!(member = find_member_link(chptr, sptr)) || !IsChanOp(member)) {
+ if (!member || !IsChanOp(member)) {
if (IsLocalChannel(chptr->chname) && HasPriv(sptr, PRIV_MODE_LCHAN)) {
modebuf_init(&mbuf, sptr, cptr, chptr,
(MODEBUF_DEST_CHANNEL | /* Send mode to channel */
MODEBUF_DEST_SERVER | /* Send mode to servers */
MODEBUF_DEST_HACK4)); /* Send a HACK(4) message */
else
+ /* Servers need to be able to op people who join using the Apass
+ * or upass, therefore we accept modes for channels with an Apass
+ * without generating a HACK3. */
modebuf_init(&mbuf, sptr, cptr, chptr,
(MODEBUF_DEST_CHANNEL | /* Send mode to clients */
- MODEBUF_DEST_SERVER | /* Send mode to servers */
- MODEBUF_DEST_HACK3)); /* Send a HACK(3) message */
+ MODEBUF_DEST_SERVER | /* Send mode to servers */
+ (*chptr->mode.apass ? 0 : MODEBUF_DEST_HACK3)));
mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2,
(MODE_PARSE_SET | /* Set the mode */
/* 029 */
{ 0 },
/* 030 */
- { 0 },
+ { RPL_APASSWARN, ":%s%s%s%s%s", "030" },
/* 031 */
{ 0 },
/* 032 */
/* 550 */
{ ERR_NOTLOWEROPLEVEL, "%s %s %hu %hu :Cannot %s someone with %s op-level", "550" },
/* 551 */
- { 0 },
+ { ERR_NOTMANAGER, "%s :You must be channel Admin to add or remove a password. %s %s %s", "551" },
/* 552 */
- { 0 },
+ { ERR_CHANSECURED, "%s :Channel is older than 48 hours and secured. Cannot change Admin pass anymore", "552" },
/* 553 */
- { 0 },
+ { ERR_UPASSSET, "%s :Cannot remove Admin pass (+A) while User pass (+u) is still set. First use /MODE %s -u <userpass>", "553" },
/* 554 */
- { 0 },
+ { ERR_UPASSNOTSET, "%s :Cannot set user pass (+u) while Admin pass (+A) is not set. First use /MODE %s +A <adminpass>", "554" },
/* 555 */
{ 0 },
/* 556 */