/*
* 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 ChannelSettings
{
unsigned int Backlog;
unsigned int Lines;
unsigned int Diff;
- unsigned int Seconds;
+ unsigned long Seconds;
void serialize(std::string& out) const
{
unsigned int MaxBacklog;
unsigned int MaxDiff;
unsigned int MaxMessageSize;
+ std::string KickMessage;
ModuleSettings() : MaxLines(0), MaxSecs(0), MaxBacklog(0), MaxDiff() { }
};
{
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]);
}
: ParamMode<RepeatMode, SimpleExtItem<ChannelSettings> >(Creator, "repeat", 'E')
, MemberInfoExt("repeat_memb", ExtensionItem::EXT_MEMBERSHIP, Creator)
{
+ syntax = "[~|*]<lines>:<sec>[:<difference>][:<backlog>]";
}
- void OnUnset(User* source, Channel* chan)
+ void OnUnset(User* source, Channel* chan) CXX11_OVERRIDE
{
// Unset the per-membership extension when the mode is removed
const Channel::MemberMap& users = chan->GetUsers();
MemberInfoExt.unset(i->second);
}
- ModeAction OnSet(User* source, Channel* channel, std::string& parameter)
+ 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;
ext.set(channel, settings);
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
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);
else
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;
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
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;
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;
- Channel* chan = reinterpret_cast<Channel*>(dest);
+ Channel* chan = target.Get<Channel>();
ChannelSettings* settings = rm.ext.get(chan);
if (!settings)
return MOD_RES_PASSTHRU;
if (!memb)
return MOD_RES_PASSTHRU;
- if (ServerInstance->OnCheckExemption(user, chan, "repeat") == MOD_RES_ALLOW)
+ 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 == 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 == ChannelSettings::ACT_BAN)
{
Modes::ChangeList changelist;
- changelist.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), "*!*@" + user->dhost);
+ 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;
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());
}
};