- if (invalid)
- break;
-
- 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';
- 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->binarymodes & CM_INVITEONLY)) *outl++ = 'i';
- chan->binarymodes |= CM_INVITEONLY;
- }
- else
- {
- if (chan->binarymodes & CM_INVITEONLY) *outl++ = 'i';
- chan->binarymodes &= ~CM_INVITEONLY;
- }
- }
- break;
-
- case 't':
- MOD_RESULT = 0;
- FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 't', "", mdir, 0));
- if (!MOD_RESULT)
- {
- if (mdir)
- {
- if (!(chan->binarymodes & CM_TOPICLOCK)) *outl++ = 't';
- chan->binarymodes |= CM_TOPICLOCK;
- }
- else
- {
- if (chan->binarymodes & CM_TOPICLOCK) *outl++ = 't';
- chan->binarymodes &= ~CM_TOPICLOCK;
- }
- }
- break;
-
- case 'n':
- MOD_RESULT = 0;
- FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'n', "", mdir, 0));
- if (!MOD_RESULT)
- {
- if (mdir)
- {
- if (!(chan->binarymodes & CM_NOEXTERNAL)) *outl++ = 'n';
- chan->binarymodes |= CM_NOEXTERNAL;
- }
- else
- {
- if (chan->binarymodes & CM_NOEXTERNAL) *outl++ = 'n';
- chan->binarymodes &= ~CM_NOEXTERNAL;
- }
- }
- break;
-
- case 'm':
- MOD_RESULT = 0;
- FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'm', "", mdir, 0));
- if (!MOD_RESULT)
- {
- if (mdir)
- {
- if (!(chan->binarymodes & CM_MODERATED)) *outl++ = 'm';
- chan->binarymodes |= CM_MODERATED;
- }
- else
- {
- if (chan->binarymodes & CM_MODERATED) *outl++ = 'm';
- chan->binarymodes &= ~CM_MODERATED;
- }
- }
- break;
-
- case 's':
- MOD_RESULT = 0;
- FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 's', "", mdir, 0));
- if (!MOD_RESULT)
- {
- if (mdir)
- {
- if (!(chan->binarymodes & CM_SECRET)) *outl++ = 's';
- chan->binarymodes |= CM_SECRET;
- if (chan->binarymodes & CM_PRIVATE)
- {
- chan->binarymodes &= ~CM_PRIVATE;
- if (mdir)
- {
- *outl++ = '-'; *outl++ = 'p'; *outl++ = '+';
- }
- }
- }
- else
- {
- if (chan->binarymodes & CM_SECRET) *outl++ = 's';
- chan->binarymodes &= ~CM_SECRET;
- }
- }
- break;
-
- case 'p':
- MOD_RESULT = 0;
- FOREACH_RESULT(I_OnRawMode,OnRawMode(user, chan, 'p', "", mdir, 0));
- if (!MOD_RESULT)
- {
- if (mdir)
- {
- if (!(chan->binarymodes & CM_PRIVATE)) *outl++ = 'p';
- chan->binarymodes |= CM_PRIVATE;
- if (chan->binarymodes & CM_SECRET)
- {
- chan->binarymodes &= ~CM_SECRET;
- if (mdir)
- {
- *outl++ = '-'; *outl++ = 's'; *outl++ = '+';
- }
- }
- }
- else
- {
- if (chan->binarymodes & CM_PRIVATE) *outl++ = 'p';
- chan->binarymodes &= ~CM_PRIVATE;
- }
- }
- break;
-
- default:
- string_list p;
- p.clear();
- bool x = chan->custom_modes[*modechar-65];
- if ((!x && !mdir) || (x && mdir))
- {
- if (!ModeIsListMode(*modechar,MT_CHANNEL))
- {
- log(DEBUG,"Mode %c isnt set on %s but trying to remove!",*modechar,chan->name);
- break;
- }
- }
- if (ModeDefined(*modechar,MT_CHANNEL))
- {
- /* A module has claimed this mode */
- if (param<pcnt)
- {
- if ((ModeDefinedOn(*modechar,MT_CHANNEL)>0) && (mdir))
- {
- p.push_back(parameters[param]);
- }
- if ((ModeDefinedOff(*modechar,MT_CHANNEL)>0) && (!mdir))
- {
- p.push_back(parameters[param]);
- }
- }
- bool handled = false;
- if (param>=pcnt)
+ if (targetchannel)
+ {
+ type = MODETYPE_CHANNEL;
+ mask = MASK_CHANNEL;
+
+ /* 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])