+ else
+ {
+ user->WriteNotice("*** Not enough parameters.");
+ return CMD_FAILURE;
+ }
+
+ }
+}
+
+bool ModuleFilter::AppliesToMe(User* user, FilterResult* filter, int iflags)
+{
+ if ((filter->flag_no_opers) && user->IsOper())
+ return false;
+ if ((iflags & FLAG_PRIVMSG) && (!filter->flag_privmsg))
+ return false;
+ if ((iflags & FLAG_NOTICE) && (!filter->flag_notice))
+ return false;
+ if ((iflags & FLAG_QUIT) && (!filter->flag_quit_message))
+ return false;
+ if ((iflags & FLAG_PART) && (!filter->flag_part_message))
+ return false;
+ return true;
+}
+
+ModuleFilter::ModuleFilter()
+ : ServerEventListener(this)
+ , Stats::EventListener(this)
+ , initing(true)
+ , filtcommand(this)
+ , RegexEngine(this, "regex")
+{
+}
+
+void ModuleFilter::init()
+{
+ ServerInstance->SNO->EnableSnomask('f', "FILTER");
+}
+
+CullResult ModuleFilter::cull()
+{
+ FreeFilters();
+ return Module::cull();
+}
+
+void ModuleFilter::FreeFilters()
+{
+ for (std::vector<FilterResult>::const_iterator i = filters.begin(); i != filters.end(); ++i)
+ delete i->regex;
+
+ filters.clear();
+}
+
+ModResult ModuleFilter::OnUserPreMessage(User* user, const MessageTarget& msgtarget, MessageDetails& details)
+{
+ // Leave remote users and servers alone
+ if (!IS_LOCAL(user))
+ return MOD_RES_PASSTHRU;
+
+ flags = (details.type == MSG_PRIVMSG) ? FLAG_PRIVMSG : FLAG_NOTICE;
+
+ FilterResult* f = this->FilterMatch(user, details.text, flags);
+ if (f)
+ {
+ std::string target;
+ if (msgtarget.type == MessageTarget::TYPE_USER)
+ {
+ User* t = msgtarget.Get<User>();
+ // Check if the target nick is exempted, if yes, ignore this message
+ if (exemptednicks.count(t->nick))
+ return MOD_RES_PASSTHRU;
+
+ target = t->nick;
+ }
+ else if (msgtarget.type == MessageTarget::TYPE_CHANNEL)
+ {
+ Channel* t = msgtarget.Get<Channel>();
+ if (exemptedchans.count(t->name))
+ return MOD_RES_PASSTHRU;
+
+ target = t->name;
+ }
+ if (f->action == FA_WARN)
+ {
+ ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("WARNING: %s's message to %s matched %s (%s)",
+ user->nick.c_str(), target.c_str(), f->freeform.c_str(), f->reason.c_str()));
+ return MOD_RES_PASSTHRU;
+ }
+ if (f->action == FA_BLOCK)
+ {
+ ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s had their message to %s filtered as it matched %s (%s)",
+ user->nick.c_str(), target.c_str(), f->freeform.c_str(), f->reason.c_str()));
+ if (notifyuser)
+ {
+ if (msgtarget.type == MessageTarget::TYPE_CHANNEL)
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, target, InspIRCd::Format("Message to channel blocked and opers notified (%s)", f->reason.c_str()));
+ else
+ user->WriteNotice("Your message to "+target+" was blocked and opers notified: "+f->reason);
+ }
+ else
+ details.echo_original = true;
+ }
+ else if (f->action == FA_SILENT)
+ {
+ if (notifyuser)
+ {
+ if (msgtarget.type == MessageTarget::TYPE_CHANNEL)
+ user->WriteNumeric(ERR_CANNOTSENDTOCHAN, target, InspIRCd::Format("Message to channel blocked (%s)", f->reason.c_str()));
+ else
+ user->WriteNotice("Your message to "+target+" was blocked: "+f->reason);
+ }
+ else
+ details.echo_original = true;
+ }
+ else if (f->action == FA_KILL)
+ {
+ ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s was killed because their message to %s matched %s (%s)",
+ user->nick.c_str(), target.c_str(), f->freeform.c_str(), f->reason.c_str()));
+ ServerInstance->Users->QuitUser(user, "Filtered: " + f->reason);
+ }
+ else if (f->action == FA_SHUN && (ServerInstance->XLines->GetFactory("SHUN")))
+ {
+ Shun* sh = new Shun(ServerInstance->Time(), f->duration, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), user->GetIPString());
+ ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s (%s) was shunned for %s (expires on %s) because their message to %s matched %s (%s)",
+ user->nick.c_str(), sh->Displayable().c_str(), InspIRCd::DurationString(f->duration).c_str(),
+ InspIRCd::TimeString(ServerInstance->Time() + f->duration).c_str(),
+ target.c_str(), f->freeform.c_str(), f->reason.c_str()));
+ if (ServerInstance->XLines->AddLine(sh, NULL))
+ {
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ delete sh;
+ }
+ else if (f->action == FA_GLINE)
+ {
+ GLine* gl = new GLine(ServerInstance->Time(), f->duration, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), "*", user->GetIPString());
+ ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s (%s) was G-lined for %s (expires on %s) because their message to %s matched %s (%s)",
+ user->nick.c_str(), gl->Displayable().c_str(), InspIRCd::DurationString(f->duration).c_str(),
+ InspIRCd::TimeString(ServerInstance->Time() + f->duration).c_str(),
+ target.c_str(), f->freeform.c_str(), f->reason.c_str()));
+ if (ServerInstance->XLines->AddLine(gl,NULL))
+ {
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ delete gl;
+ }
+ else if (f->action == FA_ZLINE)
+ {
+ ZLine* zl = new ZLine(ServerInstance->Time(), f->duration, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), user->GetIPString());
+ ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("%s (%s) was Z-lined for %s (expires on %s) because their message to %s matched %s (%s)",
+ user->nick.c_str(), zl->Displayable().c_str(), InspIRCd::DurationString(f->duration).c_str(),
+ InspIRCd::TimeString(ServerInstance->Time() + f->duration).c_str(),
+ target.c_str(), f->freeform.c_str(), f->reason.c_str()));
+ if (ServerInstance->XLines->AddLine(zl,NULL))
+ {
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ delete zl;
+ }
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, user->nick + " had their message filtered, target was " + target + ": " + f->reason + " Action: " + ModuleFilter::FilterActionToString(f->action));
+ return MOD_RES_DENY;