From 0de5151eec78b0f2dc338c703cd2df583b178e6c Mon Sep 17 00:00:00 2001 From: Sadie Powell Date: Fri, 9 Dec 2022 16:09:38 +0000 Subject: [PATCH] Add support for per-oper/opertype operator MOTDs. --- docs/conf/modules.conf.example | 9 --- docs/conf/opers.conf.example | 11 ++++ src/modules/m_opermotd.cpp | 109 +++++++++++++++++++++------------ 3 files changed, 81 insertions(+), 48 deletions(-) diff --git a/docs/conf/modules.conf.example b/docs/conf/modules.conf.example index e2268776e..a8e55838e 100644 --- a/docs/conf/modules.conf.example +++ b/docs/conf/modules.conf.example @@ -1589,15 +1589,6 @@ # on oper-up. # This module is oper-only. # -# -#-#-#-#-#-#-#-#-#-#-# 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. # -# # -# #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Override module: Adds support for oper override. diff --git a/docs/conf/opers.conf.example b/docs/conf/opers.conf.example index 12b2e69e8..6096642ce 100644 --- a/docs/conf/opers.conf.example +++ b/docs/conf/opers.conf.example @@ -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. diff --git a/src/modules/m_opermotd.cpp b/src/modules/m_opermotd.cpp index 6066800fa..f109d43ee 100644 --- a/src/modules/m_opermotd.cpp +++ b/src/modules/m_opermotd.cpp @@ -37,13 +37,16 @@ enum RPL_ENDOFOMOTD = 722 }; -class CommandOpermotd final +typedef insp::flat_map> MOTDCache; + +class CommandOperMOTD final : public Command { public: - std::vector 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& 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)