summaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
authorSadie Powell <sadie@witchery.services>2021-03-14 02:20:32 +0000
committerSadie Powell <sadie@witchery.services>2021-03-14 02:55:03 +0000
commit0a329440bd1d0fa642ce2f3e14bc88125377b5bd (patch)
tree1656e01e953871db50d65ef564d3c95286e84085 /src/modules
parentd494fa6e094e85cd29235e995fb2b447d6e1f168 (diff)
Implement support for saving filters.
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/m_filter.cpp85
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)