X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fm_repeat.cpp;h=e6568732b68aa74d6cd32d21b92033bec17175a8;hb=6223be60dee0aa4f69b53b102a9d1aadd0dde047;hp=5be0fd6227e37f04067255de8c29a1dd393433d6;hpb=9a470c5863f796308c8761457cd3e66b8f836380;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_repeat.cpp b/src/modules/m_repeat.cpp index 5be0fd622..e6568732b 100644 --- a/src/modules/m_repeat.cpp +++ b/src/modules/m_repeat.cpp @@ -1,6 +1,12 @@ /* * InspIRCd -- Internet Relay Chat Daemon * + * Copyright (C) 2019 Robby + * Copyright (C) 2018-2019 linuxdaemon + * Copyright (C) 2018 Matt Schatz + * Copyright (C) 2017-2019 Sadie Powell + * Copyright (C) 2015 James Lu + * Copyright (C) 2013-2015 Attila Molnar * Copyright (C) 2013 Daniel Vassdal * * This file is part of InspIRCd. InspIRCd is free software: you can @@ -18,13 +24,47 @@ #include "inspircd.h" +#include "modules/exemption.h" -#ifdef _WIN32 -// windows.h defines this -#undef min -#endif +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 ModeHandler +class RepeatMode : public ParamMode > { private: struct RepeatItem @@ -49,18 +89,22 @@ class RepeatMode : public ModeHandler unsigned int MaxSecs; unsigned int MaxBacklog; unsigned int MaxDiff; + unsigned int MaxMessageSize; + std::string KickMessage; ModuleSettings() : MaxLines(0), MaxSecs(0), MaxBacklog(0), MaxDiff() { } }; - std::vector > mx; + std::vector mx[2]; ModuleSettings ms; bool CompareLines(const std::string& message, const std::string& historyline, unsigned int trigger) { - if (trigger) + if (message == historyline) + return true; + else if (trigger) return (Levenshtein(message, historyline) <= trigger); - else - return (message == historyline); + + return false; } unsigned int Levenshtein(const std::string& s1, const std::string& s2) @@ -68,95 +112,58 @@ class RepeatMode : public ModeHandler unsigned int l1 = s1.size(); unsigned int l2 = s2.size(); - for (unsigned int i = 0; i <= l1; i++) - mx[i][0] = i; - for (unsigned int i = 0; i <= l2; i++) + for (unsigned int i = 0; i < l2; i++) mx[0][i] = i; - for (unsigned int i = 1; i <= l1; i++) - for (unsigned int j = 1; j <= l2; j++) - mx[i][j] = std::min(std::min(mx[i - 1][j] + 1, mx[i][j - 1] + 1), mx[i - 1][j - 1] + (s1[i - 1] == s2[j - 1] ? 0 : 1)); - return (mx[l1][l2]); - } - - 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() + for (unsigned int i = 0; i < l1; i++) { - 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; + 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[0].swap(mx[1]); } - }; + return mx[0][l2]; + } + public: SimpleExtItem MemberInfoExt; - SimpleExtItem ChanSet; RepeatMode(Module* Creator) - : ModeHandler(Creator, "repeat", 'E', PARAM_SETONLY, MODETYPE_CHANNEL) - , MemberInfoExt("repeat_memb", Creator) - , ChanSet("repeat", Creator) + : ParamMode >(Creator, "repeat", 'E') + , MemberInfoExt("repeat_memb", ExtensionItem::EXT_MEMBERSHIP, Creator) { + syntax = "[~|*]:[:][:]"; } - 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); - channel->SetModeParam(this, ""); - 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); - channel->SetModeParam(this, parameter); + ext.set(channel, settings); return MODEACTION_ALLOW; } @@ -165,8 +172,8 @@ class RepeatMode : public ModeHandler { // If the message is larger than whatever size it's set to, // let's pretend it isn't. If the first 512 (def. setting) match, it's probably spam. - if (message.size() > mx.size()) - message.erase(mx.size()); + if (message.size() > ms.MaxMessageSize) + message.erase(ms.MaxMessageSize); MemberInfo* rp = MemberInfoExt.get(memb); if (!rp) @@ -198,7 +205,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; } @@ -222,42 +229,31 @@ class RepeatMode : public ModeHandler void Resize(size_t size) { - if (size == mx.size()) + size_t newsize = size+1; + if (newsize <= mx[0].size()) return; - mx.resize(size); - - if (mx.size() > size) - { - mx.resize(size); - for (unsigned int i = 0; i < mx.size(); i++) - mx[i].resize(size); - } - else - { - for (unsigned int i = 0; i < mx.size(); i++) - { - mx[i].resize(size); - std::vector(mx[i]).swap(mx[i]); - } - std::vector >(mx).swap(mx); - } + ms.MaxMessageSize = size; + mx[0].resize(newsize); + mx[1].resize(newsize); } 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 @@ -265,6 +261,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) { @@ -276,16 +282,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(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; @@ -294,13 +300,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(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(item)) == 0) return false; // If there are still tokens, then it's invalid because we allow only 4 @@ -309,40 +315,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; @@ -351,60 +362,55 @@ class RepeatMode : public ModeHandler class RepeatModule : public Module { + CheckExemption::EventProvider exemptionprov; RepeatMode rm; public: - RepeatModule() : rm(this) {} - - void init() CXX11_OVERRIDE + RepeatModule() + : exemptionprov(this) + , rm(this) { - ServerInstance->Modules->AddService(rm); - ServerInstance->Modules->AddService(rm.ChanSet); - ServerInstance->Modules->AddService(rm.MemberInfoExt); - Implementation eventlist[] = { I_OnUserPreMessage, I_OnRehash }; - ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation)); - rm.ReadConfig(); } - void OnRehash(User* user) CXX11_OVERRIDE + 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(); + 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 parameters; - parameters.push_back(memb->chan->name); - parameters.push_back("+b"); - parameters.push_back("*!*@" + user->dhost); - ServerInstance->SendGlobalMode(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; @@ -417,7 +423,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()); } };