Add m_clearchan which removes users from a channel without sending n*(n+1)/2 QUIT messages

This commit is contained in:
Attila Molnar 2014-01-26 13:39:10 +01:00
parent 3fef0ed889
commit 7acb4ced20
5 changed files with 235 additions and 3 deletions

View File

@ -373,7 +373,7 @@ SAJOIN SAPART SAMODE SATOPIC SAKICK
KILL SAQUIT GLINE ZLINE QLINE
KLINE RLINE ELINE CBAN SHUN
FILTER OJOIN
FILTER OJOIN CLEARCHAN
CONNECT SQUIT RCONNECT RSQUIT
@ -754,6 +754,16 @@ Reloads the specified core command.">
Closes all unregistered connections to the local server.">
<helpop key="clearchan" value="/CLEARCHAN <channel> [<KILL|KICK|G|Z>] [<reason>]
Quits or kicks all non-opers from a channel, optionally G/Z-Lines them.
Useful for quickly nuking bot channels.
The default method, KILL, simply disconnects the victims from the server,
while methods G and Z also add G/Z-Lines for all the targets.
When used, the victims won't see each other getting kicked or quitting.">
######################
# User/Channel Modes #
######################

View File

@ -61,7 +61,7 @@ SAJOIN SAPART SAMODE SATOPIC SAKICK
KILL SAQUIT GLINE ZLINE QLINE
KLINE RLINE ELINE CBAN SHUN
FILTER
FILTER CLEARCHAN
CONNECT SQUIT RCONNECT RSQUIT

View File

@ -456,6 +456,11 @@
# To use, CHGNAME must be in one of your oper class blocks.
#<module name="m_chgname.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Clear chan module: Allows opers to masskick, masskill or mass-G/ZLine
# all users on a channel using /CLEARCHAN.
#<module name="m_clearchan.so">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Cloaking module: Adds usermode +x and cloaking support.
# Relies on the module m_md5.so being loaded.

View File

@ -40,7 +40,7 @@
chanmodes="*">
<class name="ServerLink" commands="CONNECT SQUIT CONNECT MKPASSWD ALLTIME SWHOIS CLOSE JUMPSERVER LOCKSERV" usermodes="*" chanmodes="*" privs="servers/auspex">
<class name="BanControl" commands="KILL GLINE KLINE ZLINE QLINE ELINE TLINE RLINE CHECK NICKLOCK SHUN CLONES CBAN" usermodes="*" chanmodes="*">
<class name="BanControl" commands="KILL GLINE KLINE ZLINE QLINE ELINE TLINE RLINE CHECK NICKLOCK SHUN CLONES CBAN CLEARCHAN" usermodes="*" chanmodes="*">
<class name="OperChat" commands="WALLOPS GLOBOPS SETIDLE" usermodes="*" chanmodes="*" privs="users/mass-message">
<class name="HostCloak" commands="SETHOST SETIDENT SETNAME CHGHOST CHGIDENT" usermodes="*" chanmodes="*" privs="users/auspex">

217
src/modules/m_clearchan.cpp Normal file
View File

@ -0,0 +1,217 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
* Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
*
* 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 "xline.h"
class CommandClearChan : public Command
{
public:
Channel* activechan;
CommandClearChan(Module* Creator)
: Command(Creator, "CLEARCHAN", 1, 3)
{
syntax = "<channel> [<KILL|KICK|G|Z>] [<reason>]";
flags_needed = 'o';
// Stop the linking mod from forwarding ENCAP'd CLEARCHAN commands, see below why
force_manual_route = true;
}
CmdResult Handle(const std::vector<std::string>& parameters, User* user)
{
Channel* chan = activechan = ServerInstance->FindChan(parameters[0]);
if (!chan)
{
user->WriteNotice("The channel " + parameters[0] + " does not exist.");
return CMD_FAILURE;
}
// See what method the oper wants to use, default to KILL
std::string method("KILL");
if (parameters.size() > 1)
{
method = parameters[1];
std::transform(method.begin(), method.end(), method.begin(), ::toupper);
}
XLineFactory* xlf = NULL;
bool kick = (method == "KICK");
if ((!kick) && (method != "KILL"))
{
if ((method != "Z") && (method != "G"))
{
user->WriteNotice("Invalid method for clearing " + chan->name);
return CMD_FAILURE;
}
xlf = ServerInstance->XLines->GetFactory(method);
if (!xlf)
return CMD_FAILURE;
}
const std::string reason = parameters.size() > 2 ? parameters.back() : "Clearing " + chan->name;
if (!user->server->IsSilentULine())
ServerInstance->SNO->WriteToSnoMask((IS_LOCAL(user) ? 'a' : 'A'), user->nick + " has cleared \002" + chan->name + "\002 (" + method + "): " + reason);
user->WriteNotice("Clearing \002" + chan->name + "\002 (" + method + "): " + reason);
{
// Route this command manually so it is sent before the QUITs we are about to generate.
// The idea is that by the time our QUITs reach the next hop, it has already removed all their
// clients from the channel, meaning victims on other servers won't see the victims on this
// server quitting.
std::vector<std::string> eparams;
eparams.push_back(chan->name);
eparams.push_back(method);
eparams.push_back(":");
eparams.back().append(reason);
ServerInstance->PI->BroadcastEncap(this->name, eparams, user, user);
}
// Attach to the appropriate hook so we're able to hide the QUIT/KICK messages
Implementation hook = (kick ? I_OnUserKick : I_OnBuildNeighborList);
ServerInstance->Modules->Attach(hook, creator);
std::string mask;
// Now remove all local non-opers from the channel
const UserMembList* users = chan->GetUsers();
for (UserMembCIter i = users->begin(); i != users->end(); )
{
User* curr = i->first;
++i;
if (!IS_LOCAL(curr) || curr->IsOper())
continue;
// If kicking users, remove them and skip the QuitUser()
if (kick)
{
chan->KickUser(ServerInstance->FakeClient, curr, reason);
continue;
}
// If we are banning users then create the XLine and add it
if (xlf)
{
XLine* xline;
try
{
mask = ((method[0] == 'Z') ? curr->GetIPString() : "*@" + curr->host);
xline = xlf->Generate(ServerInstance->Time(), 60*60, user->nick, reason, mask);
}
catch (ModuleException& ex)
{
// Nothing, move on to the next user
continue;
}
if (!ServerInstance->XLines->AddLine(xline, user))
delete xline;
}
ServerInstance->Users->QuitUser(curr, reason);
}
ServerInstance->Modules->Detach(hook, creator);
if (xlf)
ServerInstance->XLines->ApplyLines();
return CMD_SUCCESS;
}
};
class ModuleClearChan : public Module
{
CommandClearChan cmd;
public:
ModuleClearChan()
: cmd(this)
{
}
void init()
{
// Only attached while we are working; don't react to events otherwise
ServerInstance->Modules->DetachAll(this);
}
void OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception) CXX11_OVERRIDE
{
bool found = false;
for (IncludeChanList::iterator i = include.begin(); i != include.end(); ++i)
{
if ((*i)->chan == cmd.activechan)
{
// Don't show the QUIT to anyone in the channel by default
include.erase(i);
found = true;
break;
}
}
const UserMembList* users = cmd.activechan->GetUsers();
for (UserMembCIter i = users->begin(); i != users->end(); ++i)
{
LocalUser* curr = IS_LOCAL(i->first);
if (!curr)
continue;
if (curr->IsOper())
{
// If another module has removed the channel we're working on from the list of channels
// to consider for sending the QUIT to then don't add exceptions for opers, because the
// module before us doesn't want them to see it or added the exceptions already.
// If there is a value for this oper in excepts already, this won't overwrite it.
if (found)
exception.insert(std::make_pair(curr, true));
continue;
}
else if (!include.empty() && curr->chans.size() > 1)
{
// This is a victim and potentially has another common channel with the user quitting,
// add a negative exception overwriting the previous value, if any.
exception[curr] = false;
}
}
}
void OnUserKick(User* source, Membership* memb, const std::string& reason, CUList& excepts) CXX11_OVERRIDE
{
// Hide the KICK from all non-opers
User* leaving = memb->user;
const UserMembList* users = memb->chan->GetUsers();
for (UserMembCIter i = users->begin(); i != users->end(); ++i)
{
User* curr = i->first;
if ((IS_LOCAL(curr)) && (!curr->IsOper()) && (curr != leaving))
excepts.insert(curr);
}
}
Version GetVersion() CXX11_OVERRIDE
{
return Version("Adds /CLEARCHAN that allows opers to masskick, masskill or mass-G/ZLine users on a channel", VF_VENDOR|VF_OPTCOMMON);
}
};
MODULE_INIT(ModuleClearChan)