+ return Version("Text (spam) filtering", VF_VENDOR | VF_COMMON, RegexEngine ? RegexEngine->name : "");
+}
+
+std::string ModuleFilter::EncodeFilter(FilterResult* filter)
+{
+ std::ostringstream stream;
+ std::string x = filter->freeform;
+
+ /* Hax to allow spaces in the freeform without changing the design of the irc protocol */
+ for (std::string::iterator n = x.begin(); n != x.end(); n++)
+ if (*n == ' ')
+ *n = '\7';
+
+ stream << x << " " << FilterActionToString(filter->action) << " " << filter->GetFlags() << " " << filter->gline_time << " :" << filter->reason;
+ return stream.str();
+}
+
+FilterResult ModuleFilter::DecodeFilter(const std::string &data)
+{
+ std::string filteraction;
+ FilterResult res;
+ irc::tokenstream tokens(data);
+ tokens.GetToken(res.freeform);
+ tokens.GetToken(filteraction);
+ if (!StringToFilterAction(filteraction, res.action))
+ throw ModuleException("Invalid action: " + filteraction);
+
+ std::string filterflags;
+ tokens.GetToken(filterflags);
+ char c = res.FillFlags(filterflags);
+ if (c != 0)
+ throw ModuleException("Invalid flag: '" + std::string(1, c) + "'");
+
+ tokens.GetToken(res.gline_time);
+ tokens.GetToken(res.reason);
+
+ /* Hax to allow spaces in the freeform without changing the design of the irc protocol */
+ for (std::string::iterator n = res.freeform.begin(); n != res.freeform.end(); n++)
+ if (*n == '\7')
+ *n = ' ';
+
+ return res;
+}
+
+void ModuleFilter::OnSyncNetwork(Module* proto, void* opaque)
+{
+ for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); ++i)
+ {
+ proto->ProtoSendMetaData(opaque, NULL, "filter", EncodeFilter(&(*i)));
+ }
+}
+
+void ModuleFilter::OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata)
+{
+ if ((target == NULL) && (extname == "filter"))
+ {
+ try
+ {
+ FilterResult data = DecodeFilter(extdata);
+ this->AddFilter(data.freeform, data.action, data.reason, data.gline_time, data.GetFlags());
+ }
+ catch (ModuleException& e)
+ {
+ ServerInstance->Logs->Log("m_filter", DEBUG, "Error when unserializing filter: " + std::string(e.GetReason()));
+ }
+ }
+}
+
+ImplFilter::ImplFilter(ModuleFilter* mymodule, const std::string &rea, FilterAction act, long glinetime, const std::string &pat, const std::string &flgs)
+ : FilterResult(pat, rea, act, glinetime, flgs)
+{
+ if (!mymodule->RegexEngine)
+ throw ModuleException("Regex module implementing '"+mymodule->RegexEngine.GetProvider()+"' is not loaded!");
+ regex = mymodule->RegexEngine->Create(pat);
+}
+
+FilterResult* ModuleFilter::FilterMatch(User* user, const std::string &text, int flgs)
+{
+ static std::string stripped_text;
+ stripped_text.clear();
+
+ for (std::vector<ImplFilter>::iterator index = filters.begin(); index != filters.end(); index++)
+ {
+ FilterResult* filter = dynamic_cast<FilterResult*>(&(*index));
+
+ /* Skip ones that dont apply to us */
+ if (!AppliesToMe(user, filter, flgs))
+ continue;
+
+ if ((filter->flag_strip_color) && (stripped_text.empty()))
+ {
+ stripped_text = text;
+ InspIRCd::StripColor(stripped_text);
+ }
+
+ //ServerInstance->Logs->Log("m_filter", DEBUG, "Match '%s' against '%s'", text.c_str(), index->freeform.c_str());
+ if (index->regex->Matches(filter->flag_strip_color ? stripped_text : text))
+ {
+ //ServerInstance->Logs->Log("m_filter", DEBUG, "MATCH");
+ return &*index;
+ }
+ //ServerInstance->Logs->Log("m_filter", DEBUG, "NO MATCH");
+ }
+ return NULL;
+}
+
+bool ModuleFilter::DeleteFilter(const std::string &freeform)
+{
+ for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+ {
+ if (i->freeform == freeform)
+ {
+ delete i->regex;
+ filters.erase(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+std::pair<bool, std::string> ModuleFilter::AddFilter(const std::string &freeform, FilterAction type, const std::string &reason, long duration, const std::string &flgs)
+{
+ for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+ {
+ if (i->freeform == freeform)
+ {
+ return std::make_pair(false, "Filter already exists");
+ }
+ }
+
+ try
+ {
+ filters.push_back(ImplFilter(this, reason, type, duration, freeform, flgs));
+ }
+ catch (ModuleException &e)
+ {
+ ServerInstance->Logs->Log("m_filter", DEFAULT, "Error in regular expression '%s': %s", freeform.c_str(), e.GetReason());
+ return std::make_pair(false, e.GetReason());
+ }
+ return std::make_pair(true, "");
+}
+
+bool ModuleFilter::StringToFilterAction(const std::string& str, FilterAction& fa)
+{
+ irc::string s(str.c_str());
+
+ if (s == "gline")
+ fa = FA_GLINE;
+ else if (s == "block")
+ fa = FA_BLOCK;
+ else if (s == "silent")
+ fa = FA_SILENT;
+ else if (s == "kill")
+ fa = FA_KILL;
+ else if (s == "none")
+ fa = FA_NONE;
+ else
+ return false;
+
+ return true;
+}
+
+std::string ModuleFilter::FilterActionToString(FilterAction fa)
+{
+ switch (fa)
+ {
+ case FA_GLINE: return "gline";
+ case FA_BLOCK: return "block";
+ case FA_SILENT: return "silent";
+ case FA_KILL: return "kill";
+ default: return "none";
+ }
+}
+
+void ModuleFilter::ReadFilters()
+{
+ ConfigTagList tags = ServerInstance->Config->ConfTags("keyword");
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ std::string pattern = i->second->getString("pattern");
+ this->DeleteFilter(pattern);
+
+ std::string reason = i->second->getString("reason");
+ std::string action = i->second->getString("action");
+ std::string flgs = i->second->getString("flags");
+ long gline_time = ServerInstance->Duration(i->second->getString("duration"));
+ if (flgs.empty())
+ flgs = "*";
+
+ FilterAction fa;
+ if (!StringToFilterAction(action, fa))
+ fa = FA_NONE;
+
+ try
+ {
+ filters.push_back(ImplFilter(this, reason, fa, gline_time, pattern, flgs));
+ ServerInstance->Logs->Log("m_filter", DEFAULT, "Regular expression %s loaded.", pattern.c_str());
+ }
+ catch (ModuleException &e)
+ {
+ ServerInstance->Logs->Log("m_filter", DEFAULT, "Error in regular expression '%s': %s", pattern.c_str(), e.GetReason());
+ }
+ }
+}
+
+ModResult ModuleFilter::OnStats(char symbol, User* user, string_list &results)
+{
+ if (symbol == 's')
+ {
+ for (std::vector<ImplFilter>::iterator i = filters.begin(); i != filters.end(); i++)
+ {
+ results.push_back(ServerInstance->Config->ServerName+" 223 "+user->nick+" :"+RegexEngine.GetProvider()+":"+i->freeform+" "+i->GetFlags()+" "+FilterActionToString(i->action)+" "+ConvToStr(i->gline_time)+" :"+i->reason);
+ }
+ for (std::set<std::string>::iterator i = exemptfromfilter.begin(); i != exemptfromfilter.end(); ++i)
+ {
+ results.push_back(ServerInstance->Config->ServerName+" 223 "+user->nick+" :EXEMPT "+(*i));
+ }
+ }
+ return MOD_RES_PASSTHRU;
+}
+
+void ModuleFilter::OnUnloadModule(Module* mod)
+{
+ // If the regex engine became unavailable or has changed, remove all filters
+ if (!RegexEngine)
+ {
+ FreeFilters();
+ }
+ else if (RegexEngine.operator->() != factory)
+ {
+ factory = RegexEngine.operator->();
+ FreeFilters();
+ }