*mbuf++ = 'D';
else if (MyUser(cptr) && (chptr->mode.mode & MODE_WASDELJOINS))
*mbuf++ = 'd';
+ if (chptr->mode.mode & MODE_REGISTERED)
+ *mbuf++ = 'R';
if (chptr->mode.limit) {
*mbuf++ = 'l';
ircd_snprintf(0, pbuf, buflen, "%u", chptr->mode.limit);
send_reply(cptr, RPL_ENDOFBANLIST, chptr->chname);
}
-/** Remove bells and commas from channel name
- *
- * @param cn Channel name to clean, modified in place.
- */
-void clean_channelname(char *cn)
-{
- int i;
-
- for (i = 0; cn[i]; i++) {
- if (i >= IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN))
- || !IsChannelChar(cn[i])) {
- cn[i] = '\0';
- return;
- }
- if (IsChannelLower(cn[i])) {
- cn[i] = ToLower(cn[i]);
-#ifndef FIXME
- /*
- * Remove for .08+
- * toupper(0xd0)
- */
- if ((unsigned char)(cn[i]) == 0xd0)
- cn[i] = (char) 0xf0;
-#endif
- }
- }
-}
-
/** Get a channel block, creating if necessary.
* Get Channel block for chname (and allocate a new channel
* block, if it didn't exists before).
strptr[(*strptr_i)] = '\0';
}
+/** Check a channel for join-delayed members.
+ * @param[in] chan Channel to search.
+ * @return Non-zero if any members are join-delayed; false if none are.
+ */
+static int
+find_delayed_joins(const struct Channel *chan)
+{
+ const struct Membership *memb;
+ for (memb = chan->members; memb; memb = memb->next_member)
+ if (IsDelayedJoin(memb))
+ return 1;
+ return 0;
+}
+
/** Flush out the modes
* This is the workhorse of our ModeBuf suite; this actually generates the
* output MODE commands, HACK notices, or whatever. It's pretty complicated.
MODE_NOPRIVMSGS, 'n',
MODE_REGONLY, 'r',
MODE_DELJOINS, 'D',
+ MODE_REGISTERED, 'R',
/* MODE_KEY, 'k', */
/* MODE_BAN, 'b', */
MODE_LIMIT, 'l',
else
app_source = mbuf->mb_source;
+ /* Must be set if going -D and some clients are hidden */
+ if ((mbuf->mb_rem & MODE_DELJOINS)
+ && !(mbuf->mb_channel->mode.mode & (MODE_DELJOINS | MODE_WASDELJOINS))
+ && find_delayed_joins(mbuf->mb_channel)) {
+ mbuf->mb_channel->mode.mode |= MODE_WASDELJOINS;
+ mbuf->mb_add |= MODE_WASDELJOINS;
+ mbuf->mb_rem &= ~MODE_WASDELJOINS;
+ }
+
+ /* +d must be cleared if +D is set */
+ if ((mbuf->mb_add & MODE_DELJOINS)
+ && (mbuf->mb_channel->mode.mode & MODE_WASDELJOINS)) {
+ mbuf->mb_channel->mode.mode &= ~MODE_WASDELJOINS;
+ mbuf->mb_add &= ~MODE_WASDELJOINS;
+ mbuf->mb_rem |= MODE_WASDELJOINS;
+ }
+
/*
* Account for user we're bouncing; we have to get it in on the first
* bounced MODE, or we could have problems
mode &= (MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET | MODE_MODERATED |
MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS | MODE_REGONLY |
- MODE_DELJOINS | MODE_WASDELJOINS);
+ MODE_DELJOINS | MODE_WASDELJOINS | MODE_REGISTERED);
if (!(mode & ~(MODE_ADD | MODE_DEL))) /* don't add empty modes... */
return;
assert(0 != mbuf);
assert(0 != (mode & (MODE_ADD | MODE_DEL)));
- if (mode == (MODE_LIMIT | ((mbuf->mb_dest & MODEBUF_DEST_BOUNCE) ? MODE_ADD : MODE_DEL))) {
+ if (mode == (MODE_LIMIT | MODE_DEL)) {
mbuf->mb_rem |= mode;
return;
}
modebuf_flush_int(mbuf, 0);
}
-/** Check a channel for join-delayed members.
- * @param[in] chan Channel to search.
- * @return Non-zero if any members are join-delayed; false if none are.
- */
-static int
-find_delayed_joins(const struct Channel *chan)
-{
- const struct Membership *memb;
- for (memb = chan->members; memb; memb = memb->next_member)
- if (IsDelayedJoin(memb))
- return 1;
- return 0;
-}
-
/** The exported binding for modebuf_flush()
*
* @param mbuf The mode buffer to flush.
- *
+ *
* @see modebuf_flush_int()
*/
int
modebuf_flush(struct ModeBuf *mbuf)
{
- /* Check if MODE_WASDELJOINS should be set: */
- /* Must be set if going -D and some clients are hidden */
- if ((mbuf->mb_rem & MODE_DELJOINS)
- && !(mbuf->mb_channel->mode.mode & (MODE_DELJOINS | MODE_WASDELJOINS))
- && find_delayed_joins(mbuf->mb_channel)) {
- mbuf->mb_channel->mode.mode |= MODE_WASDELJOINS;
- mbuf->mb_add |= MODE_WASDELJOINS;
- mbuf->mb_rem &= ~MODE_WASDELJOINS;
- }
- /* Must be cleared if +D is set */
- if ((mbuf->mb_add & MODE_DELJOINS)
- && ((mbuf->mb_channel->mode.mode & (MODE_WASDELJOINS | MODE_WASDELJOINS))
- == (MODE_WASDELJOINS | MODE_WASDELJOINS))) {
- mbuf->mb_channel->mode.mode &= ~MODE_WASDELJOINS;
- mbuf->mb_add &= ~MODE_WASDELJOINS;
- mbuf->mb_rem |= MODE_WASDELJOINS;
- }
-
return modebuf_flush_int(mbuf, 1);
}
MODE_KEY, 'k',
MODE_APASS, 'A',
MODE_UPASS, 'U',
+ MODE_REGISTERED, 'R',
/* MODE_BAN, 'b', */
MODE_LIMIT, 'l',
MODE_REGONLY, 'r',
return;
}
-/** Simple function to invalidate bans
+/** Simple function to invalidate a channel's ban cache.
*
- * This function sets all bans as being valid.
+ * This function marks all members of the channel as being neither
+ * banned nor banned.
*
* @param chan The channel to operate on.
*/
/* What we've done for mode_parse so far... */
#define DONE_LIMIT 0x01 /**< We've set the limit */
-#define DONE_KEY 0x02 /**< We've set the key */
+#define DONE_KEY_ADD 0x02 /**< We've set the key */
#define DONE_BANLIST 0x04 /**< We've sent the ban list */
#define DONE_NOTOPER 0x08 /**< We've sent a "Not oper" error */
#define DONE_BANCLEAN 0x10 /**< We've cleaned bans... */
-#define DONE_UPASS 0x20 /**< We've set user pass */
-#define DONE_APASS 0x40 /**< We've set admin pass */
+#define DONE_UPASS_ADD 0x20 /**< We've set user pass */
+#define DONE_APASS_ADD 0x40 /**< We've set admin pass */
+#define DONE_KEY_DEL 0x80 /**< We've removed the key */
+#define DONE_UPASS_DEL 0x100 /**< We've removed the user pass */
+#define DONE_APASS_DEL 0x200 /**< We've removed the admin pass */
struct ParseState {
struct ModeBuf *mbuf;
}
}
-/** Helper function to clean key-like parameters. */
-static void
-clean_key(char *s)
+/** Helper function to validate key-like parameters.
+ *
+ * @param[in] state Parse state for feedback to user.
+ * @param[in] s Key to validate.
+ * @param[in] command String to pass for need_more_params() command.
+ * @return Zero on an invalid key, non-zero if the key was okay.
+ */
+static int
+is_clean_key(struct ParseState *state, char *s, char *command)
{
- int t_len = KEYLEN;
+ int ii;
- while (*s > ' ' && *s != ':' && *s != ',' && t_len--)
- s++;
- *s = '\0';
+ if (s[0] == '\0') {
+ if (MyUser(state->sptr))
+ need_more_params(state->sptr, command);
+ return 0;
+ }
+ else if (s[0] == ':') {
+ if (MyUser(state->sptr))
+ send_reply(state->sptr, ERR_INVALIDKEY, state->chptr->chname);
+ return 0;
+ }
+ for (ii = 0; (ii <= KEYLEN) && (s[ii] != '\0'); ++ii) {
+ if ((unsigned char)s[ii] <= ' ' || s[ii] == ',') {
+ if (MyUser(state->sptr))
+ send_reply(state->sptr, ERR_INVALIDKEY, state->chptr->chname);
+ return 0;
+ }
+ }
+ if (ii > KEYLEN) {
+ if (MyUser(state->sptr))
+ send_reply(state->sptr, ERR_INVALIDKEY, state->chptr->chname);
+ return 0;
+ }
+ return 1;
}
/*
return;
}
- if (state->done & DONE_KEY) /* allow key to be set only once */
- return;
- state->done |= DONE_KEY;
+ /* allow removing and then adding key, but not adding and then removing */
+ if (state->dir == MODE_ADD)
+ {
+ if (state->done & DONE_KEY_ADD)
+ return;
+ state->done |= DONE_KEY_ADD;
+ }
+ else
+ {
+ if (state->done & (DONE_KEY_ADD | DONE_KEY_DEL))
+ return;
+ state->done |= DONE_KEY_DEL;
+ }
- /* clean up the key string */
- clean_key(t_str);
- if (!*t_str || *t_str == ':') { /* warn if empty */
- if (MyUser(state->sptr))
- need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +k" :
- "MODE -k");
+ /* If the key is invalid, tell the user and bail. */
+ if (!is_clean_key(state, t_str, state->dir == MODE_ADD ? "MODE +k" :
+ "MODE -k"))
return;
- }
if (!state->mbuf)
return;
return;
}
- if (state->done & DONE_UPASS) /* allow upass to be set only once */
- return;
- state->done |= DONE_UPASS;
+ /* allow removing and then adding upass, but not adding and then removing */
+ if (state->dir == MODE_ADD)
+ {
+ if (state->done & DONE_UPASS_ADD)
+ return;
+ state->done |= DONE_UPASS_ADD;
+ }
+ else
+ {
+ if (state->done & (DONE_UPASS_ADD | DONE_UPASS_DEL))
+ return;
+ state->done |= DONE_UPASS_DEL;
+ }
- /* clean up the upass string */
- clean_key(t_str);
- if (!*t_str || *t_str == ':') { /* warn if empty */
- if (MyUser(state->sptr))
- need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +U" :
- "MODE -U");
+ /* If the Upass is invalid, tell the user and bail. */
+ if (!is_clean_key(state, t_str, state->dir == MODE_ADD ? "MODE +U" :
+ "MODE -U"))
return;
- }
if (!state->mbuf)
return;
}
}
- if (state->done & DONE_APASS) /* allow apass to be set only once */
- return;
- state->done |= DONE_APASS;
+ /* allow removing and then adding apass, but not adding and then removing */
+ if (state->dir == MODE_ADD)
+ {
+ if (state->done & DONE_APASS_ADD)
+ return;
+ state->done |= DONE_APASS_ADD;
+ }
+ else
+ {
+ if (state->done & (DONE_APASS_ADD | DONE_APASS_DEL))
+ return;
+ state->done |= DONE_APASS_DEL;
+ }
- /* clean up the apass string */
- clean_key(t_str);
- if (!*t_str || *t_str == ':') { /* warn if empty */
- if (MyUser(state->sptr))
- need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +A" :
- "MODE -A");
+ /* If the Apass is invalid, tell the user and bail. */
+ if (!is_clean_key(state, t_str, state->dir == MODE_ADD ? "MODE +A" :
+ "MODE -A"))
return;
- }
if (!state->mbuf)
return;
assert(newban->flags & (BAN_ADD|BAN_DEL));
if (newban->flags & BAN_ADD) {
size_t totlen = 0;
- /* If a less specific entry is found, fail. */
+ /* If a less specific *active* entry is found, fail. */
for (ban = *banlist; ban; ban = ban->next) {
- if (!bmatch(ban, newban)) {
+ if (!bmatch(ban, newban) && !(ban->flags & BAN_DEL)) {
if (do_free)
free_ban(newban);
return 1;
len -= banlen;
} else {
if (state->flags & MODE_PARSE_SET && MyUser(state->sptr) &&
+ !(state->mbuf->mb_dest & MODEBUF_DEST_OPMODE) &&
(len > (feature_int(FEAT_AVBANLEN) * feature_int(FEAT_MAXBANS)) ||
count > feature_int(FEAT_MAXBANS))) {
send_reply(state->sptr, ERR_BANLISTFULL, state->chptr->chname,
if (colon != NULL) {
*colon++ = '\0';
req_oplevel = atoi(colon);
- if (!(state->flags & MODE_PARSE_FORCE)
+ if (*flag_p == CHFL_VOICE || state->dir == MODE_DEL) {
+ /* Ignore the colon and its argument. */
+ } else if (!(state->flags & MODE_PARSE_FORCE)
&& state->member
&& (req_oplevel < OpLevel(state->member)
|| (req_oplevel == OpLevel(state->member)
&& OpLevel(state->member) < MAXOPLEVEL)
- || req_oplevel > MAXOPLEVEL))
+ || req_oplevel > MAXOPLEVEL)) {
send_reply(state->sptr, ERR_NOTLOWEROPLEVEL,
t_str, state->chptr->chname,
OpLevel(state->member), req_oplevel, "op",
OpLevel(state->member) == req_oplevel ? "the same" : "a higher");
- else if (req_oplevel <= MAXOPLEVEL)
+ } else if (req_oplevel <= MAXOPLEVEL)
oplevel = req_oplevel;
}
/* find client we're manipulating */
if (!state->mbuf)
return;
+ /* Local users are not permitted to change registration status */
+ if (flag_p[0] == MODE_REGISTERED && !(state->flags & MODE_PARSE_FORCE) &&
+ MyUser(state->sptr))
+ return;
+
if (state->dir == MODE_ADD) {
state->add |= flag_p[0];
state->del &= ~flag_p[0];
(state->add & (MODE_SECRET | MODE_PRIVATE)));
}
-/*
+/**
* This routine is intended to parse MODE or OPMODE commands and effect the
- * changes (or just build the bounce buffer). We pass the starting offset
- * as a
+ * changes (or just build the bounce buffer).
+ *
+ * \param[out] mbuf Receives parsed representation of mode change.
+ * \param[in] cptr Connection that sent the message to this server.
+ * \param[in] sptr Original source of the message.
+ * \param[in] chptr Channel whose modes are being changed.
+ * \param[in] parc Number of valid strings in \a parv.
+ * \param[in] parv Text arguments representing mode change, with the
+ * zero'th element containing a string like "+m" or "-o".
+ * \param[in] flags Set of bitwise MODE_PARSE_* flags.
+ * \param[in] member If non-null, the channel member attempting to change the modes.
*/
int
mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
MODE_KEY, 'k',
MODE_APASS, 'A',
MODE_UPASS, 'U',
+ MODE_REGISTERED, 'R',
MODE_BAN, 'b',
MODE_LIMIT, 'l',
MODE_REGONLY, 'r',
} else {
/* Server is desynced; bounce the mode and deop the source
* to fix it. */
- state.mbuf->mb_dest &= ~MODEBUF_DEST_CHANNEL;
+ state.flags &= ~MODE_PARSE_SET;
+ state.flags |= MODE_PARSE_BOUNCE;
+ state.mbuf->mb_dest &= ~(MODEBUF_DEST_CHANNEL | MODEBUF_DEST_HACK4);
state.mbuf->mb_dest |= MODEBUF_DEST_BOUNCE | MODEBUF_DEST_HACK2;
if (!IsServer(state.cptr))
state.mbuf->mb_dest |= MODEBUF_DEST_DEOP;
if (state.chptr->mode.limit && !(state.done & DONE_LIMIT))
modebuf_mode_uint(state.mbuf, MODE_DEL | MODE_LIMIT,
state.chptr->mode.limit);
- if (*state.chptr->mode.key && !(state.done & DONE_KEY))
+ if (*state.chptr->mode.key && !(state.done & DONE_KEY_DEL))
modebuf_mode_string(state.mbuf, MODE_DEL | MODE_KEY,
state.chptr->mode.key, 0);
- if (*state.chptr->mode.upass && !(state.done & DONE_UPASS))
+ if (*state.chptr->mode.upass && !(state.done & DONE_UPASS_DEL))
modebuf_mode_string(state.mbuf, MODE_DEL | MODE_UPASS,
state.chptr->mode.upass, 0);
- if (*state.chptr->mode.apass && !(state.done & DONE_APASS))
+ if (*state.chptr->mode.apass && !(state.done & DONE_APASS_DEL))
modebuf_mode_string(state.mbuf, MODE_DEL | MODE_APASS,
state.chptr->mode.apass, 0);
}
"%H -d", chan);
}
}
+
+/** Send a join for the user if (s)he is a hidden member of the channel.
+ */
+void RevealDelayedJoinIfNeeded(struct Client *sptr, struct Channel *chptr)
+{
+ struct Membership *member = find_member_link(chptr, sptr);
+ if (member && IsDelayedJoin(member))
+ RevealDelayedJoin(member);
+}