]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_filter.cpp
Update copyright headers.
[user/henk/code/inspircd.git] / src / modules / m_filter.cpp
index 786ea673bb27cce92881c678b42e01ba7cedda5b..c666f6ad265d855c31fde130a9fb78559387411f 100644 (file)
@@ -7,15 +7,15 @@
  *   Copyright (C) 2018 Michael Hazell <michaelhazell@hotmail.com>
  *   Copyright (C) 2017 B00mX0r <b00mx0r@aureus.pw>
  *   Copyright (C) 2012-2014, 2016 Attila Molnar <attilamolnar@hush.com>
- *   Copyright (C) 2012-2013, 2017-2020 Sadie Powell <sadie@witchery.services>
+ *   Copyright (C) 2012-2013, 2017-2021 Sadie Powell <sadie@witchery.services>
  *   Copyright (C) 2012, 2018-2019 Robby <robby@chatbelgie.be>
  *   Copyright (C) 2011 Adam <Adam@anope.org>
  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
  *   Copyright (C) 2009 Robin Burchell <robin+git@viroteck.net>
  *   Copyright (C) 2009 Matt Smith <dz@inspircd.org>
  *   Copyright (C) 2009 John Brooks <special@inspircd.org>
- *   Copyright (C) 2007-2010 Craig Edwards <brain@inspircd.org>
  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *   Copyright (C) 2006-2010 Craig Edwards <brain@inspircd.org>
  *
  * 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
@@ -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,72 @@ 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 was automatically generated by the " << INSPIRCD_VERSION << " filter module on " << InspIRCd::TimeString(ServerInstance->Time()) << "." << std::endl
+                       << "# Any changes to this file will be automatically 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)