Add support for per-oper/opertype operator MOTDs.

This commit is contained in:
Sadie Powell 2022-12-09 16:09:38 +00:00
parent 3e941f1cfb
commit 0de5151eec
3 changed files with 81 additions and 48 deletions

View File

@ -1589,15 +1589,6 @@
# on oper-up.
# This module is oper-only.
#<module name="opermotd">
#
#-#-#-#-#-#-#-#-#-#-# OPERMOTD CONFIGURATION -#-#-#-#-#-#-#-#-#-#-#
# #
# If you are using the opermotd module, specify the motd file here. #
# #
# onoper - If on, the message is sent on /OPER, otherwise it's #
# only sent when /OPERMOTD is used. #
# #
#<opermotd file="examples/opermotd.txt.example" onoper="yes">
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
# Override module: Adds support for oper override.

View File

@ -87,6 +87,17 @@
# maxchans: Maximum number of channels opers of this type can be in at once.
maxchans="60"
# motd: The server operator MOTD that this should be shown to this
# operator type. This can be viewed with /OPERMOTD or automatically
# on login if automotd (see below) is enabled.
# Requires the opermotd module to be loaded.
motd="examples/opermotd.txt.example"
# automotd: Whether to send the server operator MOTD (see above)
# to operators of this type when they log in.
# Requires the opermotd module to be loaded.
automotd="yes"
# modes: User modes besides +o that are set on an oper of this type
# when they oper up. Used for snomasks and other things.
# Requires the opermodes module to be loaded.

View File

@ -37,13 +37,16 @@ enum
RPL_ENDOFOMOTD = 722
};
class CommandOpermotd final
typedef insp::flat_map<std::string, std::vector<std::string>> MOTDCache;
class CommandOperMOTD final
: public Command
{
public:
std::vector<std::string> opermotd;
std::string file;
MOTDCache motds;
CommandOpermotd(Module* Creator)
CommandOperMOTD(Module* Creator)
: Command(Creator, "OPERMOTD")
{
access_needed = CmdAccess::OPERATOR;
@ -52,8 +55,8 @@ public:
CmdResult Handle(User* user, const Params& parameters) override
{
if ((parameters.empty()) || (irc::equals(parameters[0], ServerInstance->Config->ServerName)))
ShowOperMOTD(user, true);
if (parameters.empty() || irc::equals(parameters[0], ServerInstance->Config->ServerName))
return ShowOperMOTD(user, true);
return CmdResult::SUCCESS;
}
@ -64,31 +67,70 @@ public:
return ROUTE_LOCALONLY;
}
void ShowOperMOTD(User* user, bool show_missing)
CmdResult ShowOperMOTD(User* user, bool showmissing)
{
if (opermotd.empty())
if (!user->IsOper())
return CmdResult::SUCCESS; // WTF?
auto motd = motds.find(user->oper->GetConfig()->getString("motd", file, 1));
if (motd == motds.end())
{
if (show_missing)
user->WriteRemoteNumeric(ERR_NOOPERMOTD, "OPERMOTD file is missing.");
return;
if (showmissing)
user->WriteRemoteNumeric(ERR_NOOPERMOTD, "There is no server operator MOTD.");
return CmdResult::SUCCESS;
}
user->WriteRemoteNumeric(RPL_OMOTDSTART, "Server operators message of the day");
for (const auto& line : opermotd)
user->WriteRemoteNumeric(RPL_OMOTDSTART, "Server operator MOTD:");
for (const auto& line : motd->second)
user->WriteRemoteNumeric(RPL_OMOTD, line);
user->WriteRemoteNumeric(RPL_ENDOFOMOTD, "End of OPERMOTD");
user->WriteRemoteNumeric(RPL_ENDOFOMOTD, "End of server operator MOTD.");
return CmdResult::SUCCESS;
}
};
class ModuleOpermotd final
class ModuleOperMOTD final
: public Module
{
private:
CommandOpermotd cmd;
CommandOperMOTD cmd;
bool onoper;
void ProcessMOTD(MOTDCache& newmotds, const std::shared_ptr<OperType>& oper, const char* type)
{
// Don't process the file if it has already been processed.
const std::string motd = oper->GetConfig()->getString("motd", cmd.file);
if (motd.empty() || newmotds.find(motd) != newmotds.end())
return;
FileReader reader;
try
{
reader.Load(motd);
}
catch (const CoreException& ce)
{
// We can't process the file if it doesn't exist.
ServerInstance->Logs.Normal(MODNAME, "Unable to read server operator motd for oper %s \"%s\" at %s: %s",
type, oper->GetName().c_str(), oper->GetConfig()->source.str().c_str(), ce.GetReason().c_str());
return;
}
// Process the MOTD entry.
auto& newmotd = newmotds[motd];
newmotd.reserve(reader.GetVector().size());
for (const auto& line : reader.GetVector())
{
// Some clients can not handle receiving RPL_OMOTD with an empty
// trailing parameter so if a line is empty we replace it with
// a single space.
newmotd.push_back(line.empty() ? " " : line);
}
InspIRCd::ProcessColors(newmotd);
}
public:
ModuleOpermotd()
ModuleOperMOTD()
: Module(VF_VENDOR | VF_OPTCOMMON, "Adds the /OPERMOTD command which adds a special message of the day for server operators.")
, cmd(this)
{
@ -96,36 +138,25 @@ public:
void OnPostOperLogin(User* user) override
{
if (onoper && IS_LOCAL(user))
if (IS_LOCAL(user) && user->oper->GetConfig()->getBool("automotd", onoper))
cmd.ShowOperMOTD(user, false);
}
void ReadConfig(ConfigStatus& status) override
{
cmd.opermotd.clear();
auto conf = ServerInstance->Config->ConfValue("opermotd");
onoper = conf->getBool("onoper", true);
// Compatibility with the v3 config.
auto tag = ServerInstance->Config->ConfValue("opermotd");
cmd.file = tag->getString("file", "opermotd", 1);
onoper = tag->getBool("onoper", true);
try
{
FileReader reader(conf->getString("file", "opermotd", 1));
MOTDCache newmotds;
for (const auto& [_, account] : ServerInstance->Config->OperAccounts)
ProcessMOTD(newmotds, account, "account");
for (const auto& [_, type] : ServerInstance->Config->OperTypes)
ProcessMOTD(newmotds, type, "type");
cmd.motds.swap(newmotds);
// Process the MOTD entry.
cmd.opermotd.reserve(reader.GetVector().size());
for (const auto& line : reader.GetVector())
{
// Some clients can not handle receiving RPL_OMOTD with an empty
// trailing parameter so if a line is empty we replace it with
// a single space.
cmd.opermotd.push_back(line.empty() ? " " : line);
}
InspIRCd::ProcessColors(cmd.opermotd);
}
catch (const CoreException&)
{
// Nothing happens here as we do the error handling in ShowOperMOTD.
}
}
};
MODULE_INIT(ModuleOpermotd)
MODULE_INIT(ModuleOperMOTD)