/* * InspIRCd -- Internet Relay Chat Daemon * * Copyright (C) 2018, 2020 Sadie Powell * Copyright (C) 2013-2014, 2016, 2018 Attila Molnar * * 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 . */ #include "inspircd.h" enum { // From UnrealIRCd. RPL_RULES = 232, RPL_RULESTART = 308, RPL_RULESEND = 309, ERR_NORULES = 434 }; class CommandShowFile : public Command { enum Method { SF_MSG, SF_NOTICE, SF_NUMERIC }; std::string introtext; std::string endtext; unsigned int intronumeric; unsigned int textnumeric; unsigned int endnumeric; file_cache contents; Method method; public: CommandShowFile(Module* parent, const std::string& cmdname) : Command(parent, cmdname) { } CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE { if (method == SF_NUMERIC) { if (!introtext.empty() && intronumeric) user->WriteRemoteNumeric(intronumeric, introtext); for (file_cache::const_iterator i = contents.begin(); i != contents.end(); ++i) user->WriteRemoteNumeric(textnumeric, InspIRCd::Format(" %s", i->c_str())); if (!endtext.empty() && endnumeric) user->WriteRemoteNumeric(endnumeric, endtext.c_str()); } else if (IS_LOCAL(user)) { LocalUser* const localuser = IS_LOCAL(user); for (file_cache::const_iterator i = contents.begin(); i != contents.end(); ++i) { const std::string& line = *i; ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, localuser, line, ((method == SF_MSG) ? MSG_PRIVMSG : MSG_NOTICE)); localuser->Send(ServerInstance->GetRFCEvents().privmsg, msg); } } return CMD_SUCCESS; } void UpdateSettings(ConfigTag* tag, const std::vector& filecontents) { introtext = tag->getString("introtext", "Showing " + name); endtext = tag->getString("endtext", "End of " + name); intronumeric = tag->getUInt("intronumeric", RPL_RULESTART, 0, 999); textnumeric = tag->getUInt("numeric", RPL_RULES, 0, 999); endnumeric = tag->getUInt("endnumeric", RPL_RULESEND, 0, 999); std::string smethod = tag->getString("method"); method = SF_NUMERIC; if (smethod == "msg") method = SF_MSG; else if (smethod == "notice") method = SF_NOTICE; contents = filecontents; InspIRCd::ProcessColors(contents); } }; class ModuleShowFile : public Module { std::vector cmds; void ReadTag(ConfigTag* tag, std::vector& newcmds) { std::string cmdname = tag->getString("name"); if (cmdname.empty()) throw ModuleException("Empty value for 'name'"); std::transform(cmdname.begin(), cmdname.end(), cmdname.begin(), ::toupper); const std::string file = tag->getString("file", cmdname); if (file.empty()) throw ModuleException("Empty value for 'file'"); FileReader reader(file); CommandShowFile* sfcmd; Command* handler = ServerInstance->Parser.GetHandler(cmdname); if (handler) { // Command exists, check if it is ours if (handler->creator != this) throw ModuleException("Command " + cmdname + " already exists"); // This is our command, make sure we don't have the same entry twice sfcmd = static_cast(handler); if (stdalgo::isin(newcmds, sfcmd)) throw ModuleException("Command " + cmdname + " is already used in a tag"); } else { // Command doesn't exist, create it sfcmd = new CommandShowFile(this, cmdname); ServerInstance->Modules->AddService(*sfcmd); } sfcmd->UpdateSettings(tag, reader.GetVector()); newcmds.push_back(sfcmd); } public: void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { std::vector newcmds; ConfigTagList tags = ServerInstance->Config->ConfTags("showfile"); for (ConfigIter i = tags.first; i != tags.second; ++i) { ConfigTag* tag = i->second; try { ReadTag(tag, newcmds); } catch (CoreException& ex) { ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Error: " + ex.GetReason() + " at " + tag->getTagLocation()); } } // Remove all commands that were removed from the config std::vector removed(cmds.size()); std::sort(newcmds.begin(), newcmds.end()); std::set_difference(cmds.begin(), cmds.end(), newcmds.begin(), newcmds.end(), removed.begin()); stdalgo::delete_all(removed); cmds.swap(newcmds); } ~ModuleShowFile() { stdalgo::delete_all(cmds); } Version GetVersion() CXX11_OVERRIDE { return Version("Adds support for showing the contents of files to users when they execute a command.", VF_VENDOR); } }; MODULE_INIT(ModuleShowFile)