diff options
author | Peter Powell <petpow@saberuk.com> | 2019-04-19 12:29:30 +0100 |
---|---|---|
committer | Peter Powell <petpow@saberuk.com> | 2019-04-19 12:29:30 +0100 |
commit | 0f2341e76eb54fb38b97305609db9f025331d002 (patch) | |
tree | afbf54a53547dc2f79c651f38aec6b012082393f /src/coremods/core_mode.cpp | |
parent | bf7664612b00dfcf84c1a03bae2b009b9f1e1d94 (diff) |
Promote cmd_mode to its own core module.
core_user was an inappropriate location for this as it contains
mode changing code which is used by channels as well as users.
Diffstat (limited to 'src/coremods/core_mode.cpp')
-rw-r--r-- | src/coremods/core_mode.cpp | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/src/coremods/core_mode.cpp b/src/coremods/core_mode.cpp new file mode 100644 index 000000000..fa20a6902 --- /dev/null +++ b/src/coremods/core_mode.cpp @@ -0,0 +1,276 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com> + * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org> + * Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net> + * Copyright (C) 2004-2008 Craig Edwards <craigedwards@brainbox.cc> + * + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "inspircd.h" + +class CommandMode : public Command +{ + unsigned int sent[256]; + unsigned int seq; + + /** Show the list of one or more list modes to a user. + * @param user User to send to. + * @param chan Channel whose lists to show. + * @param mode_sequence Mode letters to show the lists of. + */ + void DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence); + + /** Show the current modes of a channel or a user to a user. + * @param user User to show the modes to. + * @param targetuser User whose modes to show. NULL if showing the modes of a channel. + * @param targetchannel Channel whose modes to show. NULL if showing the modes of a user. + */ + void DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel); + + public: + /** Constructor for mode. + */ + CommandMode(Module* parent); + + /** Handle command. + * @param parameters The parameters to the command + * @param user The user issuing the command + * @return A value from CmdResult to indicate command success or failure. + */ + CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE; + + RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE; +}; + +CommandMode::CommandMode(Module* parent) + : Command(parent, "MODE", 1) + , seq(0) +{ + syntax = "<target> [[(+|-)]<modes> [<mode-parameters>]]"; + memset(&sent, 0, sizeof(sent)); +} + +CmdResult CommandMode::Handle(User* user, const Params& parameters) +{ + const std::string& target = parameters[0]; + Channel* targetchannel = ServerInstance->FindChan(target); + User* targetuser = NULL; + if (!targetchannel) + { + if (IS_LOCAL(user)) + targetuser = ServerInstance->FindNickOnly(target); + else + targetuser = ServerInstance->FindNick(target); + } + + if ((!targetchannel) && (!targetuser)) + { + if (target[0] == '#') + user->WriteNumeric(Numerics::NoSuchChannel(target)); + else + 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.GetLastChangeList().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]); + } + + return CMD_SUCCESS; +} + +RouteDescriptor CommandMode::GetRouting(User* user, const Params& parameters) +{ + return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST); +} + +void CommandMode::DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence) +{ + seq++; + + for (std::string::const_iterator i = mode_sequence.begin(); i != mode_sequence.end(); ++i) + { + unsigned char mletter = *i; + if (mletter == '+') + continue; + + ModeHandler* mh = ServerInstance->Modes->FindMode(mletter, MODETYPE_CHANNEL); + if (!mh || !mh->IsListMode()) + return; + + /* Ensure the user doesnt request the same mode twice, + * so they can't flood themselves off out of idiocy. + */ + if (sent[mletter] == seq) + continue; + + sent[mletter] = seq; + ServerInstance->Modes.ShowListModeList(user, chan, mh); + } +} + +static std::string GetSnomasks(const User* user) +{ + ModeHandler* const snomask = ServerInstance->Modes.FindMode('s', MODETYPE_USER); + std::string snomaskstr = snomask->GetUserParameter(user); + // snomaskstr is empty if the snomask mode isn't set, otherwise it begins with a '+'. + // In the former case output a "+", not an empty string. + if (snomaskstr.empty()) + snomaskstr.push_back('+'); + return snomaskstr; +} + +namespace +{ + void GetModeList(Numeric::Numeric& num, Channel* chan, User* user) + { + // We should only show the value of secret parameters (i.e. key) if + // the user is a member of the channel. + bool show_secret = chan->HasUser(user); + + size_t modepos = num.push("+").GetParams().size() - 1; + std::string modes; + std::string param; + for (unsigned char chr = 65; chr < 123; ++chr) + { + // Check that the mode exists and is set. + ModeHandler* mh = ServerInstance->Modes->FindMode(chr, MODETYPE_CHANNEL); + if (!mh || !chan->IsModeSet(mh)) + continue; + + // Add the mode to the set list. + modes.push_back(mh->GetModeChar()); + + // If the mode has a parameter we need to include that too. + ParamModeBase* pm = mh->IsParameterMode(); + if (!pm) + continue; + + // If a mode has a secret parameter and the user is not privy to + // the value of it then we use <name> instead of the value. + if (pm->IsParameterSecret() && !show_secret) + { + num.push("<" + pm->name + ">"); + continue; + } + + // Retrieve the parameter and add it to the mode list. + pm->GetParameter(chan, param); + num.push(param); + param.clear(); + } + num.GetParams()[modepos].append(modes); + } +} + +void CommandMode::DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel) +{ + if (targetchannel) + { + // Display channel's current mode string + Numeric::Numeric modenum(RPL_CHANNELMODEIS); + modenum.push(targetchannel->name); + GetModeList(modenum, targetchannel, user); + user->WriteNumeric(modenum); + user->WriteNumeric(RPL_CHANNELCREATED, targetchannel->name, (unsigned long)targetchannel->age); + } + else + { + if (targetuser == user) + { + // Display user's current mode string + user->WriteNumeric(RPL_UMODEIS, targetuser->GetModeLetters()); + if (targetuser->IsOper()) + user->WriteNumeric(RPL_SNOMASKIS, GetSnomasks(targetuser), "Server notice mask"); + } + else if (user->HasPrivPermission("users/auspex")) + { + // Querying the modes of another user. + // We cannot use RPL_UMODEIS because that's only for showing the user's own modes. + user->WriteNumeric(RPL_OTHERUMODEIS, targetuser->nick, targetuser->GetModeLetters()); + if (targetuser->IsOper()) + user->WriteNumeric(RPL_OTHERSNOMASKIS, targetuser->nick, GetSnomasks(targetuser), "Server notice mask"); + } + else + { + user->WriteNumeric(ERR_USERSDONTMATCH, "Can't view modes for other users"); + } + } +} + +class CoreModMode : public Module +{ + private: + CommandMode cmdmode; + + public: + CoreModMode() + : cmdmode(this) + { + } + + Version GetVersion() CXX11_OVERRIDE + { + return Version("Provides the MODE command", VF_VENDOR|VF_CORE); + } +}; + +MODULE_INIT(CoreModMode) |