+ if ((!targetchannel) && (!targetuser))
+ {
+ user->WriteNumeric(Numerics::NoSuchNick(target));
+ return CMD_FAILURE;
+ }
+ if (parameters.size() == 1)
+ {
+ this->DisplayCurrentModes(user, targetuser, targetchannel);
+ return CMD_SUCCESS;
+ }
+
+ // Populate a temporary Modes::ChangeList with the parameters
+ Modes::ChangeList changelist;
+ ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER;
+ ServerInstance->Modes.ModeParamsToChangeList(user, type, parameters, changelist);
+
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, changelist));
+
+ ModeParser::ModeProcessFlag flags = ModeParser::MODE_NONE;
+ if (IS_LOCAL(user))
+ {
+ if (MOD_RESULT == MOD_RES_PASSTHRU)
+ {
+ if ((targetuser) && (user != targetuser))
+ {
+ // Local users may only change the modes of other users if a module explicitly allows it
+ user->WriteNumeric(ERR_USERSDONTMATCH, "Can't change mode for other users");
+ return CMD_FAILURE;
+ }
+
+ // This is a mode change by a local user and modules didn't explicitly allow/deny.
+ // Ensure access checks will happen for each mode being changed.
+ flags |= ModeParser::MODE_CHECKACCESS;
+ }
+ else if (MOD_RESULT == MOD_RES_DENY)
+ return CMD_FAILURE; // Entire mode change denied by a module
+ }
+ else
+ flags |= ModeParser::MODE_LOCALONLY;
+
+ if (IS_LOCAL(user))
+ ServerInstance->Modes->ProcessSingle(user, targetchannel, targetuser, changelist, flags);
+ else
+ ServerInstance->Modes->Process(user, targetchannel, targetuser, changelist, flags);
+
+ if ((ServerInstance->Modes.GetLastParse().empty()) && (targetchannel) && (parameters.size() == 2))
+ {
+ /* Special case for displaying the list for listmodes,
+ * e.g. MODE #chan b, or MODE #chan +b without a parameter
+ */
+ this->DisplayListModes(user, targetchannel, parameters[1]);
+ }