+ send_reply(state->sptr, (state->flags & MODE_PARSE_NOTOPER) ?
+ ERR_CHANOPRIVSNEEDED : ERR_NOTONCHANNEL, state->chptr->chname);
+
+ state->done |= DONE_NOTOPER;
+}
+
+/** Parse a limit
+ * Helper function to convert limits
+ *
+ * @param state Parsing state object.
+ * @param flag_p ?
+ */
+static void
+mode_parse_limit(struct ParseState *state, int *flag_p)
+{
+ unsigned int t_limit;
+
+ if (state->dir == MODE_ADD) { /* convert arg only if adding limit */
+ if (MyUser(state->sptr) && state->max_args <= 0) /* too many args? */
+ return;
+
+ if (state->parc <= 0) { /* warn if not enough args */
+ if (MyUser(state->sptr))
+ need_more_params(state->sptr, "MODE +l");
+ return;
+ }
+
+ t_limit = strtoul(state->parv[state->args_used++], 0, 10); /* grab arg */
+ state->parc--;
+ state->max_args--;
+
+ if ((int)t_limit<0) /* don't permit a negative limit */
+ return;
+
+ if (!(state->flags & MODE_PARSE_WIPEOUT) &&
+ (!t_limit || t_limit == state->chptr->mode.limit))
+ return;
+ } else
+ t_limit = state->chptr->mode.limit;
+
+ /* If they're not an oper, they can't change modes */
+ if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
+ send_notoper(state);
+ return;
+ }
+
+ /* Can't remove a limit that's not there */
+ if (state->dir == MODE_DEL && !state->chptr->mode.limit)
+ return;
+
+ /* Skip if this is a burst and a lower limit than this is set already */
+ if ((state->flags & MODE_PARSE_BURST) &&
+ (state->chptr->mode.mode & flag_p[0]) &&
+ (state->chptr->mode.limit < t_limit))
+ return;
+
+ if (state->done & DONE_LIMIT) /* allow limit to be set only once */
+ return;
+ state->done |= DONE_LIMIT;
+
+ if (!state->mbuf)
+ return;
+
+ modebuf_mode_uint(state->mbuf, state->dir | flag_p[0], t_limit);
+
+ if (state->flags & MODE_PARSE_SET) { /* set the limit */
+ if (state->dir & MODE_ADD) {
+ state->chptr->mode.mode |= flag_p[0];
+ state->chptr->mode.limit = t_limit;
+ } else {
+ state->chptr->mode.mode &= ~flag_p[0];
+ state->chptr->mode.limit = 0;
+ }
+ }
+}
+
+/** 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 ii;
+
+ 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;
+}
+
+/*
+ * Helper function to convert keys
+ */
+static void
+mode_parse_key(struct ParseState *state, int *flag_p)
+{
+ char *t_str;
+
+ if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
+ return;
+
+ if (state->parc <= 0) { /* warn if not enough args */
+ if (MyUser(state->sptr))
+ need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +k" :
+ "MODE -k");
+ return;
+ }
+
+ t_str = state->parv[state->args_used++]; /* grab arg */
+ state->parc--;
+ state->max_args--;
+
+ /* If they're not an oper, they can't change modes */
+ if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
+ send_notoper(state);
+ return;
+ }
+
+ /* 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;
+ }
+
+ /* 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;
+
+ /* Skip if this is a burst, we have a key already and the new key is
+ * after the old one alphabetically */
+ if ((state->flags & MODE_PARSE_BURST) &&
+ *(state->chptr->mode.key) &&
+ ircd_strcmp(state->chptr->mode.key, t_str) <= 0)
+ return;
+
+ /* can't add a key if one is set, nor can one remove the wrong key */
+ if (!(state->flags & MODE_PARSE_FORCE))
+ if ((state->dir == MODE_ADD && *state->chptr->mode.key) ||
+ (state->dir == MODE_DEL &&
+ ircd_strcmp(state->chptr->mode.key, 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.key, t_str))
+ return; /* no key change */
+
+ if (state->flags & MODE_PARSE_BOUNCE) {
+ if (*state->chptr->mode.key) /* reset old key */
+ modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
+ state->chptr->mode.key, 0);
+ else /* remove new bogus key */
+ modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
+ } else /* send new key */
+ modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);