diff options
author | Sadie Powell <sadie@witchery.services> | 2021-03-14 02:20:32 +0000 |
---|---|---|
committer | Sadie Powell <sadie@witchery.services> | 2021-03-14 02:55:03 +0000 |
commit | 0a329440bd1d0fa642ce2f3e14bc88125377b5bd (patch) | |
tree | 1656e01e953871db50d65ef564d3c95286e84085 | |
parent | d494fa6e094e85cd29235e995fb2b447d6e1f168 (diff) |
Implement support for saving filters.
-rw-r--r-- | src/modules/m_filter.cpp | 85 |
1 files changed, 83 insertions, 2 deletions
diff --git a/src/modules/m_filter.cpp b/src/modules/m_filter.cpp index 010f2a855..566b46912 100644 --- a/src/modules/m_filter.cpp +++ b/src/modules/m_filter.cpp @@ -39,6 +39,8 @@ #include "modules/stats.h" #include "modules/account.h" +#include <fstream> + enum FilterFlags { FLAG_PART = 2, @@ -132,7 +134,7 @@ class FilterResult return 0; } - std::string GetFlags() + std::string GetFlags() const { std::string flags; if (flag_no_opers) @@ -188,12 +190,15 @@ class ModuleFilter : public Module , public ServerProtocol::SyncEventListener , public Stats::EventListener + , public Timer { typedef insp::flat_set<std::string, irc::insensitive_swo> ExemptTargetSet; bool initing; bool notifyuser; bool warnonselfmsg; + bool dirty; + std::string filterconf; RegexFactory* factory; void FreeFilters(); @@ -226,6 +231,7 @@ class ModuleFilter ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE; ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE; void OnUnloadModule(Module* mod) CXX11_OVERRIDE; + bool Tick(time_t) CXX11_OVERRIDE; bool AppliesToMe(User* user, FilterResult* filter, int flags); void ReadFilters(); static bool StringToFilterAction(const std::string& str, FilterAction& fa); @@ -348,7 +354,9 @@ bool ModuleFilter::AppliesToMe(User* user, FilterResult* filter, int iflags) ModuleFilter::ModuleFilter() : ServerProtocol::SyncEventListener(this) , Stats::EventListener(this) + , Timer(0, true) , initing(true) + , dirty(false) , filtcommand(this) , RegexEngine(this, "regex") { @@ -371,6 +379,7 @@ void ModuleFilter::FreeFilters() delete i->regex; filters.clear(); + dirty = true; } ModResult ModuleFilter::OnUserPreMessage(User* user, const MessageTarget& msgtarget, MessageDetails& details) @@ -638,6 +647,10 @@ void ModuleFilter::ReadConfig(ConfigStatus& status) std::string newrxengine = tag->getString("engine"); notifyuser = tag->getBool("notifyuser", true); warnonselfmsg = tag->getBool("warnonselfmsg"); + filterconf = tag->getString("filename"); + if (!filterconf.empty()) + filterconf = ServerInstance->Config->Paths.PrependConfig(filterconf); + SetInterval(tag->getDuration("saveperiod", 5)); factory = RegexEngine ? (RegexEngine.operator->()) : NULL; @@ -779,6 +792,7 @@ bool ModuleFilter::DeleteFilter(const std::string& freeform, std::string& reason reason.assign(i->reason); delete i->regex; filters.erase(i); + dirty = true; return true; } } @@ -798,6 +812,7 @@ std::pair<bool, std::string> ModuleFilter::AddFilter(const std::string& freeform try { filters.push_back(FilterResult(RegexEngine, freeform, reason, type, duration, flgs, config)); + dirty = true; } catch (ModuleException &e) { @@ -879,7 +894,7 @@ void ModuleFilter::ReadFilters() if (!StringToFilterAction(action, fa)) fa = FA_NONE; - std::pair<bool, std::string> result = static_cast<ModuleFilter*>(this)->AddFilter(pattern, fa, reason, duration, flgs, true); + std::pair<bool, std::string> result = static_cast<ModuleFilter*>(this)->AddFilter(pattern, fa, reason, duration, flgs, !i->second->getBool("generated")); if (result.first) removedfilters.erase(pattern); else @@ -927,4 +942,70 @@ void ModuleFilter::OnUnloadModule(Module* mod) } } +bool ModuleFilter::Tick(time_t) +{ + if (!dirty) // No need to write. + return true; + + if (filterconf.empty()) // Nothing to write to. + { + dirty = false; + return true; + } + + const std::string newfilterconf = filterconf + ".tmp"; + std::ofstream stream(newfilterconf.c_str()); + if (!stream.is_open()) // Filesystem probably not writable. + { + ServerInstance->SNO->WriteToSnoMask('f', "Unable to save filters to \"%s\": %s (%d)", + newfilterconf.c_str(), strerror(errno), errno); + return true; + } + + stream << "# This file is automatically generated by the filter module. Any changes will be overwritten." << std::endl + << "# If you want to convert this to a normal config file you *MUST* remove the generated=\"yes\" keys!" << std::endl + << std::endl + << "<config format=\"xml\">" << std::endl; + + for (std::vector<FilterResult>::iterator i = filters.begin(); i != filters.end(); ++i) + { + // # <keyword reason="You qwertied!" action="block" flags="pn"> + const FilterResult& filter = (*i); + if (filter.from_config) + continue; + + stream << "<keyword generated=\"yes" + << "\" pattern=\"" << ServerConfig::Escape(filter.freeform) + << "\" reason=\"" << ServerConfig::Escape(filter.reason) + << "\" action=\"" << FilterActionToString(filter.action) + << "\" flags=\"" << filter.GetFlags(); + if (filter.duration) + stream << "\" duration=\"" << InspIRCd::DurationString(filter.duration); + stream << "\">" << std::endl; + } + + if (stream.fail()) // Filesystem probably not writable. + { + ServerInstance->SNO->WriteToSnoMask('f', "Unable to save filters to \"%s\": %s (%d)", + newfilterconf.c_str(), strerror(errno), errno); + return true; + } + stream.close(); + +#ifdef _WIN32 + remove(filterconf.c_str()); +#endif + + // Use rename to move temporary to new db - this is guaranteed not to fuck up, even in case of a crash. + if (rename(newfilterconf.c_str(), filterconf.c_str()) < 0) + { + ServerInstance->SNO->WriteToSnoMask('f', "Unable to replace old filter config \"%s\" with \"%s\": %s (%d)", + filterconf.c_str(), newfilterconf.c_str(), strerror(errno), errno); + return true; + } + + dirty = false; + return true; +} + MODULE_INIT(ModuleFilter) |