]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_repeat.cpp
Fix the cloaking module on C++98 compilers.
[user/henk/code/inspircd.git] / src / modules / m_repeat.cpp
index d91fe7e8a5f1a868b5f84614292071762124d11a..5a532389a8eef17b7ff35343afd05689589fc522 100644 (file)
@@ -1,6 +1,13 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *   Copyright (C) 2021 iwalkalone <iwalkalone69@gmail.com>
+ *   Copyright (C) 2019 Robby <robby@chatbelgie.be>
+ *   Copyright (C) 2018-2019 linuxdaemon <linuxdaemon.irc@gmail.com>
+ *   Copyright (C) 2018 Matt Schatz <genius3000@g3k.solutions>
+ *   Copyright (C) 2017-2019 Sadie Powell <sadie@witchery.services>
+ *   Copyright (C) 2015 James Lu <GLolol@overdrivenetworks.com>
+ *   Copyright (C) 2013-2015 Attila Molnar <attilamolnar@hush.com>
  *   Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
  *
  * This file is part of InspIRCd.  InspIRCd is free software: you can
 
 
 #include "inspircd.h"
+#include "modules/exemption.h"
 
-class RepeatMode : public ModeHandler
+class ChannelSettings
+{
+ public:
+       enum RepeatAction
+       {
+               ACT_KICK,
+               ACT_BLOCK,
+               ACT_BAN
+       };
+
+       RepeatAction Action;
+       unsigned int Backlog;
+       unsigned int Lines;
+       unsigned int Diff;
+       unsigned long Seconds;
+
+       void serialize(std::string& out) const
+       {
+               if (Action == ACT_BAN)
+                       out.push_back('*');
+               else if (Action == ACT_BLOCK)
+                       out.push_back('~');
+
+               out.append(ConvToStr(Lines)).push_back(':');
+               out.append(ConvToStr(Seconds));
+               if (Diff)
+               {
+                       out.push_back(':');
+                       out.append(ConvToStr(Diff));
+                       if (Backlog)
+                       {
+                               out.push_back(':');
+                               out.append(ConvToStr(Backlog));
+                       }
+               }
+       }
+};
+
+class RepeatMode : public ParamMode<RepeatMode, SimpleExtItem<ChannelSettings> >
 {
  private:
        struct RepeatItem
@@ -45,6 +91,7 @@ class RepeatMode : public ModeHandler
                unsigned int MaxBacklog;
                unsigned int MaxDiff;
                unsigned int MaxMessageSize;
+               std::string KickMessage;
                ModuleSettings() : MaxLines(0), MaxSecs(0), MaxBacklog(0), MaxDiff() { }
        };
 
@@ -72,7 +119,7 @@ class RepeatMode : public ModeHandler
                {
                        mx[1][0] = i + 1;
                        for (unsigned int j = 0; j < l2; j++)
-                   mx[1][j + 1] = std::min(std::min(mx[1][j] + 1, mx[0][j + 1] + 1), mx[0][j] + ((s1[i] == s2[j]) ? 0 : 1));
+                               mx[1][j + 1] = std::min(std::min(mx[1][j] + 1, mx[0][j + 1] + 1), mx[0][j] + ((s1[i] == s2[j]) ? 0 : 1));
 
                        mx[0].swap(mx[1]);
                }
@@ -80,82 +127,44 @@ class RepeatMode : public ModeHandler
        }
 
  public:
-       enum RepeatAction
-       {
-               ACT_KICK,
-               ACT_BLOCK,
-               ACT_BAN
-       };
-
-       class ChannelSettings
-       {
-        public:
-               RepeatAction Action;
-               unsigned int Backlog;
-               unsigned int Lines;
-               unsigned int Diff;
-               unsigned int Seconds;
-
-               std::string serialize()
-               {
-                       std::string ret = ((Action == ACT_BAN) ? "*" : (Action == ACT_BLOCK ? "~" : "")) + ConvToStr(Lines) + ":" + ConvToStr(Seconds);
-                       if (Diff)
-                       {
-                               ret += ":" + ConvToStr(Diff);
-                               if (Backlog)
-                                       ret += ":" + ConvToStr(Backlog);
-                       }
-                       return ret;
-               }
-       };
-
        SimpleExtItem<MemberInfo> MemberInfoExt;
-       SimpleExtItem<ChannelSettings> ChanSet;
 
        RepeatMode(Module* Creator)
-               : ModeHandler(Creator, "repeat", 'E', PARAM_SETONLY, MODETYPE_CHANNEL)
-               , MemberInfoExt("repeat_memb", Creator)
-               , ChanSet("repeat", Creator)
+               : ParamMode<RepeatMode, SimpleExtItem<ChannelSettings> >(Creator, "repeat", 'E')
+               , MemberInfoExt("repeat_memb", ExtensionItem::EXT_MEMBERSHIP, Creator)
        {
+               syntax = "[~|*]<lines>:<sec>[:<difference>][:<backlog>]";
        }
 
-       ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding)
+       void OnUnset(User* source, Channel* chan) CXX11_OVERRIDE
        {
-               if (!adding)
-               {
-                       if (!channel->IsModeSet(this))
-                               return MODEACTION_DENY;
-
-                       // Unset the per-membership extension when the mode is removed
-                       const UserMembList* users = channel->GetUsers();
-                       for (UserMembCIter i = users->begin(); i != users->end(); ++i)
-                               MemberInfoExt.unset(i->second);
-
-                       ChanSet.unset(channel);
-                       return MODEACTION_ALLOW;
-               }
-
-               if (channel->GetModeParameter(this) == parameter)
-                       return MODEACTION_DENY;
+               // Unset the per-membership extension when the mode is removed
+               const Channel::MemberMap& users = chan->GetUsers();
+               for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+                       MemberInfoExt.unset(i->second);
+       }
 
+       ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
+       {
                ChannelSettings settings;
                if (!ParseSettings(source, parameter, settings))
                {
-                       source->WriteNotice("*** Invalid syntax. Syntax is {[~*]}[lines]:[time]{:[difference]}{:[backlog]}");
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
                        return MODEACTION_DENY;
                }
 
                if ((settings.Backlog > 0) && (settings.Lines > settings.Backlog))
                {
-                       source->WriteNotice("*** You can't set needed lines higher than backlog");
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter,
+                               "You can't set lines higher than backlog."));
                        return MODEACTION_DENY;
                }
 
                LocalUser* localsource = IS_LOCAL(source);
-               if ((localsource) && (!ValidateSettings(localsource, settings)))
+               if ((localsource) && (!ValidateSettings(localsource, channel, parameter, settings)))
                        return MODEACTION_DENY;
 
-               ChanSet.set(channel, settings);
+               ext.set(channel, settings);
 
                return MODEACTION_ALLOW;
        }
@@ -197,7 +206,7 @@ class RepeatMode : public ModeHandler
                        {
                                if (++matches >= rs->Lines)
                                {
-                                       if (rs->Action != ACT_BLOCK)
+                                       if (rs->Action != ChannelSettings::ACT_BLOCK)
                                                rp->Counter = 0;
                                        return true;
                                }
@@ -232,18 +241,20 @@ class RepeatMode : public ModeHandler
        void ReadConfig()
        {
                ConfigTag* conf = ServerInstance->Config->ConfValue("repeat");
-               ms.MaxLines = conf->getInt("maxlines", 20);
-               ms.MaxBacklog = conf->getInt("maxbacklog", 20);
-               ms.MaxSecs = conf->getInt("maxsecs", 0);
+               ms.MaxLines = conf->getUInt("maxlines", 20);
+               ms.MaxBacklog = conf->getUInt("maxbacklog", 20);
+               ms.MaxSecs = conf->getDuration("maxtime", conf->getDuration("maxsecs", 0));
 
-               ms.MaxDiff = conf->getInt("maxdistance", 50);
+               ms.MaxDiff = conf->getUInt("maxdistance", 50);
                if (ms.MaxDiff > 100)
                        ms.MaxDiff = 100;
 
-               unsigned int newsize = conf->getInt("size", 512);
+               unsigned int newsize = conf->getUInt("size", 512);
                if (newsize > ServerInstance->Config->Limits.MaxLine)
                        newsize = ServerInstance->Config->Limits.MaxLine;
                Resize(newsize);
+
+               ms.KickMessage = conf->getString("kickmessage", "Repeat flood");
        }
 
        std::string GetModuleSettings() const
@@ -251,6 +262,16 @@ class RepeatMode : public ModeHandler
                return ConvToStr(ms.MaxLines) + ":" + ConvToStr(ms.MaxSecs) + ":" + ConvToStr(ms.MaxDiff) + ":" + ConvToStr(ms.MaxBacklog);
        }
 
+       std::string GetKickMessage() const
+       {
+               return ms.KickMessage;
+       }
+
+       void SerializeParam(Channel* chan, const ChannelSettings* chset, std::string& out)
+       {
+               chset->serialize(out);
+       }
+
  private:
        bool ParseSettings(User* source, std::string& parameter, ChannelSettings& settings)
        {
@@ -262,16 +283,16 @@ class RepeatMode : public ModeHandler
 
                if ((item[0] == '*') || (item[0] == '~'))
                {
-                       settings.Action = ((item[0] == '*') ? ACT_BAN : ACT_BLOCK);
+                       settings.Action = ((item[0] == '*') ? ChannelSettings::ACT_BAN : ChannelSettings::ACT_BLOCK);
                        item.erase(item.begin());
                }
                else
-                       settings.Action = ACT_KICK;
+                       settings.Action = ChannelSettings::ACT_KICK;
 
-               if ((settings.Lines = ConvToInt(item)) == 0)
+               if ((settings.Lines = ConvToNum<unsigned int>(item)) == 0)
                        return false;
 
-               if ((!stream.GetToken(item)) || ((settings.Seconds = InspIRCd::Duration(item)) == 0))
+               if ((!stream.GetToken(item)) || !InspIRCd::Duration(item, settings.Seconds) || (settings.Seconds == 0))
                        // Required parameter missing
                        return false;
 
@@ -280,13 +301,13 @@ class RepeatMode : public ModeHandler
                if (stream.GetToken(item))
                {
                        // There is a diff parameter, see if it's valid (> 0)
-                       if ((settings.Diff = ConvToInt(item)) == 0)
+                       if ((settings.Diff = ConvToNum<unsigned int>(item)) == 0)
                                return false;
 
                        if (stream.GetToken(item))
                        {
                                // There is a backlog parameter, see if it's valid
-                               if ((settings.Backlog = ConvToInt(item)) == 0)
+                               if ((settings.Backlog = ConvToNum<unsigned int>(item)) == 0)
                                        return false;
 
                                // If there are still tokens, then it's invalid because we allow only 4
@@ -295,40 +316,45 @@ class RepeatMode : public ModeHandler
                        }
                }
 
-               parameter = settings.serialize();
                return true;
        }
 
-       bool ValidateSettings(LocalUser* source, const ChannelSettings& settings)
+       bool ValidateSettings(LocalUser* source, Channel* channel, const std::string& parameter, const ChannelSettings& settings)
        {
-               if (settings.Backlog && !ms.MaxBacklog)
+               if (ms.MaxLines && settings.Lines > ms.MaxLines)
                {
-                       source->WriteNotice("*** The server administrator has disabled backlog matching");
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter, InspIRCd::Format(
+                               "The line number you specified is too big. Maximum allowed is %u.", ms.MaxLines)));
                        return false;
                }
 
-               if (settings.Diff)
+               if (ms.MaxSecs && settings.Seconds > ms.MaxSecs)
                {
-                       if (settings.Diff > ms.MaxDiff)
-                       {
-                               if (ms.MaxDiff == 0)
-                                       source->WriteNotice("*** The server administrator has disabled matching on edit distance");
-                               else
-                                       source->WriteNotice("*** The distance you specified is too great. Maximum allowed is " + ConvToStr(ms.MaxDiff));
-                               return false;
-                       }
+                       source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter, InspIRCd::Format(
+                               "The seconds you specified are too big. Maximum allowed is %u.", ms.MaxSecs)));
+                       return false;
+               }
 
-                       if (ms.MaxLines && settings.Lines > ms.MaxLines)
-                       {
-                               source->WriteNotice("*** The line number you specified is too great. Maximum allowed is " + ConvToStr(ms.MaxLines));
-                               return false;
-                       }
+               if (settings.Diff && settings.Diff > ms.MaxDiff)
+               {
+                       if (ms.MaxDiff == 0)
+                               source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter,
+                                       "The server administrator has disabled matching on edit distance."));
+                       else
+                               source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter, InspIRCd::Format(
+                                       "The distance you specified is too big. Maximum allowed is %u.", ms.MaxDiff)));
+                       return false;
+               }
 
-                       if (ms.MaxSecs && settings.Seconds > ms.MaxSecs)
-                       {
-                               source->WriteNotice("*** The seconds you specified is too great. Maximum allowed is " + ConvToStr(ms.MaxSecs));
-                               return false;
-                       }
+               if (settings.Backlog && settings.Backlog > ms.MaxBacklog)
+               {
+                       if (ms.MaxBacklog == 0)
+                               source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter,
+                                       "The server administrator has disabled backlog matching."));
+                       else
+                               source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter, InspIRCd::Format(
+                                       "The backlog you specified is too big. Maximum allowed is %u.", ms.MaxBacklog)));
+                       return false;
                }
 
                return true;
@@ -337,50 +363,55 @@ class RepeatMode : public ModeHandler
 
 class RepeatModule : public Module
 {
+       CheckExemption::EventProvider exemptionprov;
        RepeatMode rm;
 
  public:
-       RepeatModule() : rm(this) {}
+       RepeatModule()
+               : exemptionprov(this)
+               , rm(this)
+       {
+       }
 
        void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
                rm.ReadConfig();
        }
 
-       ModResult OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype) CXX11_OVERRIDE
+       ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
        {
-               if (target_type != TYPE_CHANNEL || !IS_LOCAL(user))
+               if (target.type != MessageTarget::TYPE_CHANNEL || !IS_LOCAL(user))
                        return MOD_RES_PASSTHRU;
 
-               Membership* memb = ((Channel*)dest)->GetUser(user);
-               if (!memb || !memb->chan->IsModeSet(&rm))
+               Channel* chan = target.Get<Channel>();
+               ChannelSettings* settings = rm.ext.get(chan);
+               if (!settings)
                        return MOD_RES_PASSTHRU;
 
-               if (ServerInstance->OnCheckExemption(user, memb->chan, "repeat") == MOD_RES_ALLOW)
+               Membership* memb = chan->GetUser(user);
+               if (!memb)
                        return MOD_RES_PASSTHRU;
 
-               RepeatMode::ChannelSettings* settings = rm.ChanSet.get(memb->chan);
-               if (!settings)
+               ModResult res = CheckExemption::Call(exemptionprov, user, chan, "repeat");
+               if (res == MOD_RES_ALLOW)
                        return MOD_RES_PASSTHRU;
 
-               if (rm.MatchLine(memb, settings, text))
+               if (rm.MatchLine(memb, settings, details.text))
                {
-                       if (settings->Action == RepeatMode::ACT_BLOCK)
+                       if (settings->Action == ChannelSettings::ACT_BLOCK)
                        {
-                               user->WriteNotice("*** This line is too similiar to one of your last lines.");
+                               user->WriteNotice("*** This line is too similar to one of your last lines.");
                                return MOD_RES_DENY;
                        }
 
-                       if (settings->Action == RepeatMode::ACT_BAN)
+                       if (settings->Action == ChannelSettings::ACT_BAN)
                        {
-                               std::vector<std::string> parameters;
-                               parameters.push_back(memb->chan->name);
-                               parameters.push_back("+b");
-                               parameters.push_back("*!*@" + user->dhost);
-                               ServerInstance->Modes->Process(parameters, ServerInstance->FakeClient);
+                               Modes::ChangeList changelist;
+                               changelist.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), "*!*@" + user->GetDisplayedHost());
+                               ServerInstance->Modes->Process(ServerInstance->FakeClient, chan, NULL, changelist);
                        }
 
-                       memb->chan->KickUser(ServerInstance->FakeClient, user, "Repeat flood");
+                       memb->chan->KickUser(ServerInstance->FakeClient, user, rm.GetKickMessage());
                        return MOD_RES_DENY;
                }
                return MOD_RES_PASSTHRU;
@@ -393,7 +424,7 @@ class RepeatModule : public Module
 
        Version GetVersion() CXX11_OVERRIDE
        {
-               return Version("Provides the +E channel mode - for blocking of similiar messages", VF_COMMON|VF_VENDOR, rm.GetModuleSettings());
+               return Version("Adds channel mode E (repeat) which helps protect against spammers which spam the same message repeatedly.", VF_COMMON|VF_VENDOR, rm.GetModuleSettings());
        }
 };