/* * InspIRCd -- Internet Relay Chat Daemon * * Copyright (C) 2019 linuxdaemon * Copyright (C) 2019 Robby * Copyright (C) 2018-2020 Sadie Powell * Copyright (C) 2017 B00mX0r * Copyright (C) 2014, 2016 Attila Molnar * * 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 . */ #include "inspircd.h" class CommandMode : public Command { private: unsigned int sent[256]; unsigned int seq; ChanModeReference secretmode; ChanModeReference privatemode; /** 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); bool CanSeeChan(User* user, Channel* chan) { // A user can always see the channel modes if they are: // (1) In the channel. // (2) An oper with the channels/auspex privilege. if (chan->HasUser(user) || user->HasPrivPermission("channels/auspex")) return true; // Otherwise, they can only see the modes when the channel is not +p or +s. return !chan->IsModeSet(secretmode) && !chan->IsModeSet(privatemode); } 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) , secretmode(creator, "secret") , privatemode(creator, "private") { syntax = " [[(+|-)] []]"; 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 || !CanSeeChan(user, 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 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) { } void On005Numeric(std::map& tokens) CXX11_OVERRIDE { tokens["CHANMODES"] = ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL); tokens["USERMODES"] = ServerInstance->Modes->GiveModeList(MODETYPE_USER); } Version GetVersion() CXX11_OVERRIDE { return Version("Provides the MODE command", VF_VENDOR|VF_CORE); } }; MODULE_INIT(CoreModMode)