/*
* InspIRCd -- Internet Relay Chat Daemon
*
* Copyright (C) 2017, 2019-2024 Sadie Powell <sadie@sadiepowell.dev>
* Copyright (C) 2013-2014 Attila Molnar <attilamolnar@hush.com>
* Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
*
* 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"
#include "listmode.h"
#include "numerichelper.h"
class CommandRMode final
: public Command
{
private:
static ModeHandler* FindMode(const std::string& mode)
{
if (mode.length() == 1)
{
ModeHandler* mh = ServerInstance->Modes.FindMode(mode[0], MODETYPE_CHANNEL);
if (!mh)
mh = ServerInstance->Modes.FindPrefix(mode[0]);
return mh;
}
return ServerInstance->Modes.FindMode(mode, MODETYPE_CHANNEL);
}
public:
CommandRMode(Module* Creator)
: Command(Creator, "RMODE", 2, 3)
{
syntax = { "<channel> <mode> [<pattern>]" };
}
CmdResult Handle(User* user, const Params& parameters) override
{
auto* chan = ServerInstance->Channels.Find(parameters[0]);
if (!chan)
{
user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
return CmdResult::FAILURE;
}
ModeHandler* mh = FindMode(parameters[1]);
if (!mh)
{
user->WriteNumeric(ERR_UNKNOWNMODE, parameters[1], "is not a recognised channel mode.");
return CmdResult::FAILURE;
}
if (chan->GetPrefixValue(user) < mh->GetLevelRequired(false))
{
user->WriteNumeric(Numerics::ChannelPrivilegesNeeded(chan, mh->GetLevelRequired(false), INSP_FORMAT("unset channel mode {} ({})",
mh->GetModeChar(), mh->name)));
return CmdResult::FAILURE;
}
std::string pattern = parameters.size() > 2 ? parameters[2] : "*";
PrefixMode* pm;
ListModeBase* lm;
ListModeBase::ModeList* ml;
Modes::ChangeList changelist;
if ((pm = mh->IsPrefixMode()))
{
// As user prefix modes don't have a GetList() method, let's iterate through the channel's users.
for (const auto& [u, memb] : chan->GetUsers())
{
if (!InspIRCd::Match(u->nick, pattern))
continue;
if (memb->HasMode(pm) && !((u == user) && (pm->GetPrefixRank() > VOICE_VALUE)))
changelist.push_remove(mh, u->nick);
}
}
else if ((lm = mh->IsListModeBase()) && ((ml = lm->GetList(chan)) != nullptr))
{
auto* targuser = parameters.size() > 2 ? ServerInstance->Users.FindNick(parameters[2]) : nullptr;
for (const auto& entry : *ml)
{
if (targuser ? chan->CheckBan(targuser, entry.mask) : InspIRCd::Match(entry.mask, pattern))
changelist.push_remove(mh, entry.mask);
}
}
else
{
if (chan->IsModeSet(mh))
changelist.push_remove(mh);
}
ServerInstance->Modes.Process(user, chan, nullptr, changelist);
return CmdResult::SUCCESS;
}
};
class ModuleRMode final
: public Module
{
private:
CommandRMode cmd;
public:
ModuleRMode()
: Module(VF_VENDOR, "Allows removal of channel list modes using glob patterns.")
, cmd(this)
{
}
};
MODULE_INIT(ModuleRMode)