#include "inspircd.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 int 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<RepeatMode, SimpleExtItem<ChannelSettings> >
{
private:
struct RepeatItem
unsigned int MaxSecs;
unsigned int MaxBacklog;
unsigned int MaxDiff;
+ unsigned int MaxMessageSize;
ModuleSettings() : MaxLines(0), MaxSecs(0), MaxBacklog(0), MaxDiff() { }
};
- std::vector<std::vector<unsigned int> > mx;
+ std::vector<unsigned int> 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)
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<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)
{
}
- ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding)
+ void OnUnset(User* source, Channel* chan)
{
- 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)
+ {
ChannelSettings settings;
if (!ParseSettings(source, parameter, settings))
{
if ((localsource) && (!ValidateSettings(localsource, settings)))
return MODEACTION_DENY;
- ChanSet.set(channel, settings);
- channel->SetModeParam(this, parameter);
+ ext.set(channel, settings);
return MODEACTION_ALLOW;
}
{
// 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)
{
if (++matches >= rs->Lines)
{
- if (rs->Action != ACT_BLOCK)
+ if (rs->Action != ChannelSettings::ACT_BLOCK)
rp->Counter = 0;
return true;
}
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<unsigned int>(mx[i]).swap(mx[i]);
- }
- std::vector<std::vector<unsigned int> >(mx).swap(mx);
- }
+ ms.MaxMessageSize = size;
+ mx[0].resize(newsize);
+ mx[1].resize(newsize);
}
void ReadConfig()
return ConvToStr(ms.MaxLines) + ":" + ConvToStr(ms.MaxSecs) + ":" + ConvToStr(ms.MaxDiff) + ":" + ConvToStr(ms.MaxBacklog);
}
+ void SerializeParam(Channel* chan, const ChannelSettings* chset, std::string& out)
+ {
+ chset->serialize(out);
+ }
+
private:
bool ParseSettings(User* source, std::string& parameter, ChannelSettings& settings)
{
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)
return false;
}
}
- parameter = settings.serialize();
return true;
}
public:
RepeatModule() : rm(this) {}
- void init() CXX11_OVERRIDE
- {
- 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();
}
if (target_type != TYPE_CHANNEL || !IS_LOCAL(user))
return MOD_RES_PASSTHRU;
- Membership* memb = ((Channel*)dest)->GetUser(user);
- if (!memb || !memb->chan->IsModeSet(&rm))
+ Channel* chan = reinterpret_cast<Channel*>(dest);
+ 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)
+ if (ServerInstance->OnCheckExemption(user, chan, "repeat") == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
if (rm.MatchLine(memb, settings, 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->SendGlobalMode(parameters, ServerInstance->FakeClient);
+ Modes::ChangeList changelist;
+ changelist.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), "*!*@" + user->dhost);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, chan, NULL, changelist);
}
memb->chan->KickUser(ServerInstance->FakeClient, user, "Repeat flood");
Version GetVersion() CXX11_OVERRIDE
{
- return Version("Provides the +E channel mode - for blocking of similiar messages", VF_COMMON|VF_VENDOR, rm.GetModuleSettings());
+ return Version("Provides the +E channel mode - for blocking of similar messages", VF_COMMON|VF_VENDOR, rm.GetModuleSettings());
}
};