mirror of
https://github.com/anope/anope.git
synced 2026-06-24 18:46:37 +02:00
beaf09de7b
Admins can now queue multiple messages and send them when they are ready. This is fully compatible with the previous global behaviour. Admins can now also send messages to individual servers. This is useful for informing users of maintenance due to downtime.
600 lines
17 KiB
C++
600 lines
17 KiB
C++
/* OperServ core functions
|
|
*
|
|
* (C) 2003-2024 Anope Team
|
|
* Contact us at team@anope.org
|
|
*
|
|
* Please read COPYING and README for further details.
|
|
*
|
|
* Based on the original code of Epona by Lara.
|
|
* Based on the original code of Services by Andy Church.
|
|
*/
|
|
|
|
#include "module.h"
|
|
#include "modules/os_session.h"
|
|
|
|
enum DefconLevel
|
|
{
|
|
DEFCON_NO_NEW_CHANNELS,
|
|
DEFCON_NO_NEW_NICKS,
|
|
DEFCON_NO_MLOCK_CHANGE,
|
|
DEFCON_FORCE_CHAN_MODES,
|
|
DEFCON_REDUCE_SESSION,
|
|
DEFCON_NO_NEW_CLIENTS,
|
|
DEFCON_OPER_ONLY,
|
|
DEFCON_SILENT_OPER_ONLY,
|
|
DEFCON_AKILL_NEW_CLIENTS,
|
|
DEFCON_NO_NEW_MEMOS
|
|
};
|
|
|
|
bool DefConModesSet = false;
|
|
|
|
struct DefconConfig final
|
|
{
|
|
std::vector<std::bitset<32> > DefCon;
|
|
std::set<Anope::string> DefConModesOn, DefConModesOff;
|
|
std::map<Anope::string, Anope::string> DefConModesOnParams;
|
|
|
|
int defaultlevel, sessionlimit;
|
|
Anope::string chanmodes, message, offmessage, akillreason;
|
|
std::vector<Anope::string> defcons;
|
|
time_t akillexpire, timeout;
|
|
bool globalondefcon;
|
|
|
|
unsigned max_session_kill;
|
|
time_t session_autokill_expiry;
|
|
Anope::string sle_reason, sle_detailsloc;
|
|
|
|
DefconConfig()
|
|
{
|
|
this->DefCon.resize(6);
|
|
this->defcons.resize(5);
|
|
}
|
|
|
|
bool Check(DefconLevel level)
|
|
{
|
|
return this->Check(this->defaultlevel, level);
|
|
}
|
|
|
|
bool Check(int dlevel, DefconLevel level)
|
|
{
|
|
return this->DefCon[dlevel].test(level);
|
|
}
|
|
|
|
void Add(int dlevel, DefconLevel level)
|
|
{
|
|
this->DefCon[dlevel][level] = true;
|
|
}
|
|
|
|
void Del(int dlevel, DefconLevel level)
|
|
{
|
|
this->DefCon[dlevel][level] = false;
|
|
}
|
|
|
|
bool SetDefConParam(const Anope::string &name, const Anope::string &buf)
|
|
{
|
|
return DefConModesOnParams.emplace(name, buf).second;
|
|
}
|
|
|
|
void UnsetDefConParam(const Anope::string &name)
|
|
{
|
|
DefConModesOnParams.erase(name);
|
|
}
|
|
|
|
bool GetDefConParam(const Anope::string &name, Anope::string &buf)
|
|
{
|
|
std::map<Anope::string, Anope::string>::iterator it = DefConModesOnParams.find(name);
|
|
|
|
buf.clear();
|
|
|
|
if (it != DefConModesOnParams.end())
|
|
{
|
|
buf = it->second;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
static DefconConfig DConfig;
|
|
|
|
static void runDefCon();
|
|
static Anope::string defconReverseModes(const Anope::string &modes);
|
|
|
|
static ServiceReference<GlobalService> GlobalService("GlobalService", "Global");
|
|
|
|
static Timer *timeout;
|
|
|
|
class DefConTimeout final
|
|
: public Timer
|
|
{
|
|
int level;
|
|
|
|
public:
|
|
DefConTimeout(Module *mod, int newlevel)
|
|
: Timer(mod, DConfig.timeout)
|
|
, level(newlevel)
|
|
{
|
|
timeout = this;
|
|
}
|
|
|
|
~DefConTimeout() override
|
|
{
|
|
timeout = NULL;
|
|
}
|
|
|
|
void Tick() override
|
|
{
|
|
if (DConfig.defaultlevel != level)
|
|
{
|
|
DConfig.defaultlevel = level;
|
|
FOREACH_MOD(OnDefconLevel, (level));
|
|
Log(Config->GetClient("OperServ"), "operserv/defcon") << "Defcon level timeout, returning to level " << level;
|
|
|
|
if (DConfig.globalondefcon)
|
|
{
|
|
if (!DConfig.offmessage.empty())
|
|
GlobalService->SendSingle(DConfig.offmessage);
|
|
else
|
|
GlobalService->SendSingle(Anope::printf(Language::Translate(_("The Defcon level is now at: \002%d\002")), DConfig.defaultlevel));
|
|
|
|
if (!DConfig.message.empty())
|
|
GlobalService->SendSingle(DConfig.message);
|
|
}
|
|
|
|
runDefCon();
|
|
}
|
|
}
|
|
};
|
|
|
|
class CommandOSDefcon final
|
|
: public Command
|
|
{
|
|
static void SendLevels(CommandSource &source)
|
|
{
|
|
if (DConfig.Check(DEFCON_NO_NEW_CHANNELS))
|
|
source.Reply(_("* No new channel registrations"));
|
|
if (DConfig.Check(DEFCON_NO_NEW_NICKS))
|
|
source.Reply(_("* No new nick registrations"));
|
|
if (DConfig.Check(DEFCON_NO_MLOCK_CHANGE))
|
|
source.Reply(_("* No mode lock changes"));
|
|
if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && !DConfig.chanmodes.empty())
|
|
source.Reply(_("* Force channel modes (%s) to be set on all channels"), DConfig.chanmodes.c_str());
|
|
if (DConfig.Check(DEFCON_REDUCE_SESSION))
|
|
source.Reply(_("* Use the reduced session limit of %d"), DConfig.sessionlimit);
|
|
if (DConfig.Check(DEFCON_NO_NEW_CLIENTS))
|
|
source.Reply(_("* Kill any new clients connecting"));
|
|
if (DConfig.Check(DEFCON_OPER_ONLY))
|
|
source.Reply(_("* Ignore non-opers with a message"));
|
|
if (DConfig.Check(DEFCON_SILENT_OPER_ONLY))
|
|
source.Reply(_("* Silently ignore non-opers"));
|
|
if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS))
|
|
source.Reply(_("* AKILL any new clients connecting"));
|
|
if (DConfig.Check(DEFCON_NO_NEW_MEMOS))
|
|
source.Reply(_("* No new memos sent"));
|
|
}
|
|
|
|
public:
|
|
CommandOSDefcon(Module *creator) : Command(creator, "operserv/defcon", 1, 1)
|
|
{
|
|
this->SetDesc(_("Manipulate the DefCon system"));
|
|
this->SetSyntax(_("[\0021\002|\0022\002|\0023\002|\0024\002|\0025\002]"));
|
|
}
|
|
|
|
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
|
{
|
|
const Anope::string &lvl = params[0];
|
|
|
|
if (lvl.empty())
|
|
{
|
|
source.Reply(_("Services are now at DEFCON \002%d\002."), DConfig.defaultlevel);
|
|
this->SendLevels(source);
|
|
return;
|
|
}
|
|
|
|
auto newLevel = Anope::Convert<int>(lvl, 0);
|
|
if (newLevel < 1 || newLevel > 5)
|
|
{
|
|
this->OnSyntaxError(source, "");
|
|
return;
|
|
}
|
|
|
|
DConfig.defaultlevel = newLevel;
|
|
|
|
FOREACH_MOD(OnDefconLevel, (newLevel));
|
|
|
|
delete timeout;
|
|
|
|
if (DConfig.timeout)
|
|
timeout = new DefConTimeout(this->module, 5);
|
|
|
|
source.Reply(_("Services are now at DEFCON \002%d\002."), DConfig.defaultlevel);
|
|
this->SendLevels(source);
|
|
Log(LOG_ADMIN, source, this) << "to change defcon level to " << newLevel;
|
|
|
|
/* Global notice the user what is happening. Also any Message that
|
|
the Admin would like to add. Set in config file. */
|
|
if (DConfig.globalondefcon)
|
|
{
|
|
if (DConfig.defaultlevel == 5 && !DConfig.offmessage.empty())
|
|
GlobalService->SendSingle(DConfig.offmessage);
|
|
else if (DConfig.defaultlevel != 5)
|
|
{
|
|
GlobalService->SendSingle(Anope::printf(_("The Defcon level is now at: \002%d\002"), DConfig.defaultlevel));
|
|
if (!DConfig.message.empty())
|
|
GlobalService->SendSingle(DConfig.message);
|
|
}
|
|
}
|
|
|
|
/* Run any defcon functions, e.g. FORCE CHAN MODE */
|
|
runDefCon();
|
|
return;
|
|
}
|
|
|
|
bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
|
|
{
|
|
this->SendSyntax(source);
|
|
source.Reply(" ");
|
|
source.Reply(_("The defcon system can be used to implement a pre-defined\n"
|
|
"set of restrictions to services useful during an attempted\n"
|
|
"attack on the network."));
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class OSDefcon final
|
|
: public Module
|
|
{
|
|
ServiceReference<SessionService> session_service;
|
|
ServiceReference<XLineManager> akills;
|
|
CommandOSDefcon commandosdefcon;
|
|
|
|
void ParseModeString()
|
|
{
|
|
int add = -1; /* 1 if adding, 0 if deleting, -1 if neither */
|
|
ChannelMode *cm;
|
|
ChannelModeParam *cmp;
|
|
Anope::string modes, param;
|
|
|
|
spacesepstream ss(DConfig.chanmodes);
|
|
|
|
DConfig.DefConModesOn.clear();
|
|
DConfig.DefConModesOff.clear();
|
|
ss.GetToken(modes);
|
|
|
|
/* Loop while there are modes to set */
|
|
for (auto mode : modes)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case '+':
|
|
add = 1;
|
|
continue;
|
|
case '-':
|
|
add = 0;
|
|
continue;
|
|
default:
|
|
if (add < 0)
|
|
continue;
|
|
}
|
|
|
|
if ((cm = ModeManager::FindChannelModeByChar(mode)))
|
|
{
|
|
if (cm->type == MODE_STATUS || cm->type == MODE_LIST)
|
|
{
|
|
Log(this) << "DefConChanModes mode character '" << mode << "' cannot be locked";
|
|
continue;
|
|
}
|
|
else if (add)
|
|
{
|
|
DConfig.DefConModesOn.insert(cm->name);
|
|
DConfig.DefConModesOff.erase(cm->name);
|
|
|
|
if (cm->type == MODE_PARAM)
|
|
{
|
|
cmp = anope_dynamic_static_cast<ChannelModeParam *>(cm);
|
|
|
|
if (!ss.GetToken(param))
|
|
{
|
|
Log(this) << "DefConChanModes mode character '" << mode << "' has no parameter while one is expected";
|
|
continue;
|
|
}
|
|
|
|
if (!cmp->IsValid(param))
|
|
continue;
|
|
|
|
DConfig.SetDefConParam(cmp->name, param);
|
|
}
|
|
}
|
|
else if (DConfig.DefConModesOn.count(cm->name))
|
|
{
|
|
DConfig.DefConModesOn.erase(cm->name);
|
|
|
|
if (cm->type == MODE_PARAM)
|
|
DConfig.UnsetDefConParam(cm->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We can't mlock +L if +l is not mlocked as well. */
|
|
if ((cm = ModeManager::FindChannelModeByName("REDIRECT")) && DConfig.DefConModesOn.count(cm->name) && !DConfig.DefConModesOn.count("LIMIT"))
|
|
{
|
|
DConfig.DefConModesOn.erase("REDIRECT");
|
|
|
|
Log(this) << "DefConChanModes must lock mode +l as well to lock mode +L";
|
|
}
|
|
}
|
|
|
|
public:
|
|
OSDefcon(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), session_service("SessionService", "session"), akills("XLineManager", "xlinemanager/sgline"), commandosdefcon(this)
|
|
{
|
|
|
|
}
|
|
|
|
void OnReload(Configuration::Conf *conf) override
|
|
{
|
|
Configuration::Block *block = conf->GetModule(this);
|
|
DefconConfig dconfig;
|
|
|
|
dconfig.defaultlevel = block->Get<int>("defaultlevel");
|
|
dconfig.defcons[4] = block->Get<const Anope::string>("level4");
|
|
dconfig.defcons[3] = block->Get<const Anope::string>("level3");
|
|
dconfig.defcons[2] = block->Get<const Anope::string>("level2");
|
|
dconfig.defcons[1] = block->Get<const Anope::string>("level1");
|
|
dconfig.sessionlimit = block->Get<int>("sessionlimit");
|
|
dconfig.akillreason = block->Get<const Anope::string>("akillreason");
|
|
dconfig.akillexpire = block->Get<time_t>("akillexpire");
|
|
dconfig.chanmodes = block->Get<const Anope::string>("chanmodes");
|
|
dconfig.timeout = block->Get<time_t>("timeout");
|
|
dconfig.globalondefcon = block->Get<bool>("globalondefcon");
|
|
dconfig.message = block->Get<const Anope::string>("message");
|
|
dconfig.offmessage = block->Get<const Anope::string>("offmessage");
|
|
|
|
block = conf->GetModule("os_session");
|
|
|
|
dconfig.max_session_kill = block->Get<int>("maxsessionkill");
|
|
dconfig.session_autokill_expiry = block->Get<time_t>("sessionautokillexpiry");
|
|
dconfig.sle_reason = block->Get<const Anope::string>("sessionlimitexceeded");
|
|
dconfig.sle_detailsloc = block->Get<const Anope::string>("sessionlimitdetailsloc");
|
|
|
|
if (dconfig.defaultlevel < 1 || dconfig.defaultlevel > 5)
|
|
throw ConfigException("The value for <defcon:defaultlevel> must be between 1 and 5");
|
|
else if (dconfig.akillexpire <= 0)
|
|
throw ConfigException("The value for <defcon:akillexpire> must be greater than zero!");
|
|
|
|
for (unsigned level = 1; level < 5; ++level)
|
|
{
|
|
spacesepstream operations(dconfig.defcons[level]);
|
|
Anope::string operation;
|
|
while (operations.GetToken(operation))
|
|
{
|
|
if (operation.equals_ci("nonewchannels"))
|
|
dconfig.Add(level, DEFCON_NO_NEW_CHANNELS);
|
|
else if (operation.equals_ci("nonewnicks"))
|
|
dconfig.Add(level, DEFCON_NO_NEW_NICKS);
|
|
else if (operation.equals_ci("nomlockchanges"))
|
|
dconfig.Add(level, DEFCON_NO_MLOCK_CHANGE);
|
|
else if (operation.equals_ci("forcechanmodes"))
|
|
dconfig.Add(level, DEFCON_FORCE_CHAN_MODES);
|
|
else if (operation.equals_ci("reducedsessions"))
|
|
dconfig.Add(level, DEFCON_REDUCE_SESSION);
|
|
else if (operation.equals_ci("nonewclients"))
|
|
dconfig.Add(level, DEFCON_NO_NEW_CLIENTS);
|
|
else if (operation.equals_ci("operonly"))
|
|
dconfig.Add(level, DEFCON_OPER_ONLY);
|
|
else if (operation.equals_ci("silentoperonly"))
|
|
dconfig.Add(level, DEFCON_SILENT_OPER_ONLY);
|
|
else if (operation.equals_ci("akillnewclients"))
|
|
dconfig.Add(level, DEFCON_AKILL_NEW_CLIENTS);
|
|
else if (operation.equals_ci("nonewmemos"))
|
|
dconfig.Add(level, DEFCON_NO_NEW_MEMOS);
|
|
}
|
|
|
|
if (dconfig.Check(level, DEFCON_REDUCE_SESSION) && dconfig.sessionlimit <= 0)
|
|
throw ConfigException("The value for <defcon:sessionlimit> must be greater than zero!");
|
|
else if (dconfig.Check(level, DEFCON_AKILL_NEW_CLIENTS) && dconfig.akillreason.empty())
|
|
throw ConfigException("The value for <defcon:akillreason> must not be empty!");
|
|
else if (dconfig.Check(level, DEFCON_FORCE_CHAN_MODES) && dconfig.chanmodes.empty())
|
|
throw ConfigException("The value for <defcon:chanmodes> must not be empty!");
|
|
}
|
|
|
|
DConfig = dconfig;
|
|
this->ParseModeString();
|
|
}
|
|
|
|
EventReturn OnChannelModeSet(Channel *c, MessageSource &source, ChannelMode *mode, const Anope::string ¶m) override
|
|
{
|
|
if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && DConfig.DefConModesOff.count(mode->name) && source.GetUser() && !source.GetBot())
|
|
{
|
|
c->RemoveMode(Config->GetClient("OperServ"), mode, param);
|
|
|
|
return EVENT_STOP;
|
|
}
|
|
|
|
return EVENT_CONTINUE;
|
|
}
|
|
|
|
EventReturn OnChannelModeUnset(Channel *c, MessageSource &source, ChannelMode *mode, const Anope::string &) override
|
|
{
|
|
if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && DConfig.DefConModesOn.count(mode->name) && source.GetUser() && !source.GetBot())
|
|
{
|
|
Anope::string param;
|
|
|
|
if (DConfig.GetDefConParam(mode->name, param))
|
|
c->SetMode(Config->GetClient("OperServ"), mode, param);
|
|
else
|
|
c->SetMode(Config->GetClient("OperServ"), mode);
|
|
|
|
return EVENT_STOP;
|
|
|
|
}
|
|
|
|
return EVENT_CONTINUE;
|
|
}
|
|
|
|
EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> ¶ms) override
|
|
{
|
|
if (DConfig.Check(DEFCON_OPER_ONLY) && !source.IsOper())
|
|
{
|
|
source.Reply(_("Services are in DefCon mode, please try again later."));
|
|
return EVENT_STOP;
|
|
}
|
|
else if (DConfig.Check(DEFCON_SILENT_OPER_ONLY) && !source.IsOper())
|
|
{
|
|
return EVENT_STOP;
|
|
}
|
|
else if (command->name == "nickserv/register" || command->name == "nickserv/group")
|
|
{
|
|
if (DConfig.Check(DEFCON_NO_NEW_NICKS))
|
|
{
|
|
source.Reply(_("Services are in DefCon mode, please try again later."));
|
|
return EVENT_STOP;
|
|
}
|
|
}
|
|
else if (command->name == "chanserv/mode" && params.size() > 1 && params[1].equals_ci("LOCK"))
|
|
{
|
|
if (DConfig.Check(DEFCON_NO_MLOCK_CHANGE))
|
|
{
|
|
source.Reply(_("Services are in DefCon mode, please try again later."));
|
|
return EVENT_STOP;
|
|
}
|
|
}
|
|
else if (command->name == "chanserv/register")
|
|
{
|
|
if (DConfig.Check(DEFCON_NO_NEW_CHANNELS))
|
|
{
|
|
source.Reply(_("Services are in DefCon mode, please try again later."));
|
|
return EVENT_STOP;
|
|
}
|
|
}
|
|
else if (command->name == "memoserv/send")
|
|
{
|
|
if (DConfig.Check(DEFCON_NO_NEW_MEMOS))
|
|
{
|
|
source.Reply(_("Services are in DefCon mode, please try again later."));
|
|
return EVENT_STOP;
|
|
}
|
|
}
|
|
|
|
return EVENT_CONTINUE;
|
|
}
|
|
|
|
void OnUserConnect(User *u, bool &exempt) override
|
|
{
|
|
if (exempt || u->Quitting() || !u->server->IsSynced() || u->server->IsULined())
|
|
return;
|
|
|
|
BotInfo *OperServ = Config->GetClient("OperServ");
|
|
if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS) && akills)
|
|
{
|
|
Log(OperServ, "operserv/defcon") << "DEFCON: adding akill for *@" << u->host;
|
|
XLine x("*@" + u->host, OperServ ? OperServ->nick : "defcon", Anope::CurTime + DConfig.akillexpire, DConfig.akillreason, XLineManager::GenerateUID());
|
|
akills->Send(NULL, &x);
|
|
}
|
|
|
|
if (DConfig.Check(DEFCON_NO_NEW_CLIENTS) || DConfig.Check(DEFCON_AKILL_NEW_CLIENTS))
|
|
{
|
|
u->Kill(OperServ, DConfig.akillreason);
|
|
return;
|
|
}
|
|
|
|
if (DConfig.sessionlimit <= 0 || !session_service)
|
|
return;
|
|
|
|
Session *session = session_service->FindSession(u->ip.addr());
|
|
Exception *exception = session_service->FindException(u);
|
|
|
|
if (DConfig.Check(DEFCON_REDUCE_SESSION) && !exception)
|
|
{
|
|
if (session && session->count > static_cast<unsigned>(DConfig.sessionlimit))
|
|
{
|
|
if (!DConfig.sle_reason.empty())
|
|
{
|
|
Anope::string message = DConfig.sle_reason.replace_all_cs("%IP%", u->ip.addr());
|
|
u->SendMessage(OperServ, message);
|
|
}
|
|
if (!DConfig.sle_detailsloc.empty())
|
|
u->SendMessage(OperServ, DConfig.sle_detailsloc);
|
|
|
|
++session->hits;
|
|
if (akills && DConfig.max_session_kill && session->hits >= DConfig.max_session_kill)
|
|
{
|
|
XLine x("*@" + session->addr.mask(), OperServ ? OperServ->nick : "", Anope::CurTime + DConfig.session_autokill_expiry, "Defcon session limit exceeded", XLineManager::GenerateUID());
|
|
akills->Send(NULL, &x);
|
|
Log(OperServ, "akill/defcon") << "[DEFCON] Added a temporary AKILL for \002*@" << session->addr.mask() << "\002 due to excessive connections";
|
|
}
|
|
else
|
|
{
|
|
u->Kill(OperServ, "Defcon session limit exceeded");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnChannelModeAdd(ChannelMode *cm) override
|
|
{
|
|
if (DConfig.chanmodes.find(cm->mchar) != Anope::string::npos)
|
|
this->ParseModeString();
|
|
}
|
|
|
|
void OnChannelSync(Channel *c) override
|
|
{
|
|
if (DConfig.Check(DEFCON_FORCE_CHAN_MODES))
|
|
c->SetModes(Config->GetClient("OperServ"), false, DConfig.chanmodes);
|
|
}
|
|
};
|
|
|
|
static void runDefCon()
|
|
{
|
|
BotInfo *OperServ = Config->GetClient("OperServ");
|
|
if (DConfig.Check(DEFCON_FORCE_CHAN_MODES))
|
|
{
|
|
if (!DConfig.chanmodes.empty() && !DefConModesSet)
|
|
{
|
|
if (DConfig.chanmodes[0] == '+' || DConfig.chanmodes[0] == '-')
|
|
{
|
|
Log(OperServ, "operserv/defcon") << "DEFCON: setting " << DConfig.chanmodes << " on all channels";
|
|
DefConModesSet = true;
|
|
for (const auto &[_, chan] : ChannelList)
|
|
chan->SetModes(OperServ, false, DConfig.chanmodes);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!DConfig.chanmodes.empty() && DefConModesSet)
|
|
{
|
|
if (DConfig.chanmodes[0] == '+' || DConfig.chanmodes[0] == '-')
|
|
{
|
|
DefConModesSet = false;
|
|
Anope::string newmodes = defconReverseModes(DConfig.chanmodes);
|
|
if (!newmodes.empty())
|
|
{
|
|
Log(OperServ, "operserv/defcon") << "DEFCON: setting " << newmodes << " on all channels";
|
|
for (const auto &[_, chan] : ChannelList)
|
|
chan->SetModes(OperServ, true, newmodes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static Anope::string defconReverseModes(const Anope::string &modes)
|
|
{
|
|
if (modes.empty())
|
|
return "";
|
|
Anope::string newmodes;
|
|
for (auto mode : modes)
|
|
{
|
|
if (mode == '+')
|
|
newmodes += '-';
|
|
else if (mode == '-')
|
|
newmodes += '+';
|
|
else
|
|
newmodes += mode;
|
|
}
|
|
return newmodes;
|
|
}
|
|
|
|
MODULE_INIT(OSDefcon)
|