- MOD_RESULT = 0;
- FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'l', parameters[param], true, 1));
- if (!MOD_RESULT)
- {
-
- chan->limit = atoi(parameters[param]);
-
- // reported by mech: large values cause underflow
- if (chan->limit < 0)
- chan->limit = 0x7FFF;
- }
-
- if (chan->limit)
- {
- *outl++ = 'l';
- chan->modes[CM_LIMIT] = 1;
- outpars[pc++] = parameters[param++];
- l_set = true;
- }
- }
- break;
-
- case 'i':
- MOD_RESULT = 0;
- FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'i', "", mdir, 0));
- if (!MOD_RESULT)
- {
- if (mdir)
- {
- if (!(chan->modes[CM_INVITEONLY])) *outl++ = 'i';
- chan->modes[CM_INVITEONLY] = 1;
- }
- else
- {
- if (chan->modes[CM_INVITEONLY]) *outl++ = 'i';
- chan->modes[CM_INVITEONLY] = 0;
- }
- }
- break;
-
- case 't':
- MOD_RESULT = 0;
- FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 't', "", mdir, 0));
- if (!MOD_RESULT)
- {
- if (mdir)
- {
- if (!(chan->modes[CM_TOPICLOCK])) *outl++ = 't';
- chan->modes[CM_TOPICLOCK] = 1;
- }
- else
- {
- if (chan->modes[CM_TOPICLOCK]) *outl++ = 't';
- chan->modes[CM_TOPICLOCK] = 0;
- }
- }
- break;
-
- case 'n':
- MOD_RESULT = 0;
- FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'n', "", mdir, 0));
- if (!MOD_RESULT)
- {
- if (mdir)
- {
- if (!(chan->modes[CM_NOEXTERNAL])) *outl++ = 'n';
- chan->modes[CM_NOEXTERNAL] = 1;
- }
- else
- {
- if (chan->modes[CM_NOEXTERNAL]) *outl++ = 'n';
- chan->modes[CM_NOEXTERNAL] = 0;
- }
- }
- break;
-
- case 'm':
- MOD_RESULT = 0;
- FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'm', "", mdir, 0));
- if (!MOD_RESULT)
- {
- if (mdir)
- {
- if (!(chan->modes[CM_MODERATED])) *outl++ = 'm';
- chan->modes[CM_MODERATED] = 1;
- }
- else
- {
- if (chan->modes[CM_MODERATED]) *outl++ = 'm';
- chan->modes[CM_MODERATED] = 0;
- }
- }
- break;
-
- case 's':
- MOD_RESULT = 0;
- FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 's', "", mdir, 0));
- if (!MOD_RESULT)
- {
- if (mdir)
+ /* Extra security checks on channel modes
+ * (e.g. are they a (half)op?
+ */
+
+ if ((IS_LOCAL(user)) && (!ServerInstance->ULine(user->server)) && (!servermode))
+ {
+ /* We don't have halfop */
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user, NULL, targetchannel, AC_GENERAL_MODE));
+ if (MOD_RESULT == ACR_DENY)
+ return;
+ SkipAccessChecks = (MOD_RESULT == ACR_ALLOW);
+ }
+ }
+ else if (targetuser)
+ {
+ type = MODETYPE_USER;
+ mask = MASK_USER;
+ if ((user != targetuser) && (!ServerInstance->ULine(user->server)))
+ {
+ user->WriteNumeric(502, "%s :Can't change mode for other users", user->nick);
+ return;
+ }
+ }
+ else
+ {
+ /* No such nick/channel */
+ user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick, parameters[0]);
+ return;
+ }
+
+ std::string mode_sequence = parameters[1];
+ std::string parameter;
+ std::ostringstream parameter_list;
+ std::string output_sequence;
+ bool adding = true, state_change = false;
+ unsigned char handler_id = 0;
+ int parameter_counter = 2; /* Index of first parameter */
+ int parameter_count = 0;
+ bool last_successful_state_change = false;
+
+ /* A mode sequence that doesnt start with + or -. Assume +. - Thanks for the suggestion spike (bug#132) */
+ if ((*mode_sequence.begin() != '+') && (*mode_sequence.begin() != '-'))
+ mode_sequence.insert(0, "+");
+
+ for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)
+ {
+ unsigned char modechar = *letter;
+
+ switch (modechar)
+ {
+ /* NB:
+ * For + and - mode characters, we don't just stick the character into the output sequence.
+ * This is because the user may do something dumb, like: +-+ooo or +oo-+. To prevent this
+ * appearing in the output sequence, we store a flag which says there was a state change,
+ * which is set on any + or -, however, the + or - that we finish on is only appended to
+ * the output stream in the event it is followed by a non "+ or -" character, such as o or v.
+ */
+ case '+':
+ /* The following expression prevents: +o+o nick nick, compressing it to +oo nick nick,
+ * however, will allow the + if it is the first item in the sequence, regardless.
+ */
+ if ((!adding) || (!output_sequence.length()))
+ state_change = true;
+ adding = true;
+ if (!output_sequence.length())
+ last_successful_state_change = false;
+ continue;
+ break;
+ case '-':
+ if ((adding) || (!output_sequence.length()))
+ state_change = true;
+ adding = false;
+ if (!output_sequence.length())
+ last_successful_state_change = true;
+ continue;
+ break;
+ default:
+
+ /**
+ * Watch carefully for the sleight of hand trick.
+ * 65 is the ascii value of 'A'. We take this from
+ * the char we're looking at to get a number between
+ * 1 and 127. We then logic-or it to get the hashed
+ * position, dependent on wether its a channel or
+ * a user mode. This is a little stranger, but a lot
+ * faster, than using a map of pairs.
+ */
+ handler_id = (modechar - 65) | mask;
+
+ if (modehandlers[handler_id])