+ /* Deleting a filter */
+ Module* me = creator;
+ std::string reason;
+
+ if (static_cast<ModuleFilter*>(me)->DeleteFilter(parameters[0], reason))
+ {
+ user->WriteNotice("*** Removed filter '" + parameters[0] + "': " + reason);
+ ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'f' : 'F', "%s removed filter '%s': %s",
+ user->nick.c_str(), parameters[0].c_str(), reason.c_str());
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteNotice("*** Filter '" + parameters[0] + "' not found on the list.");
+ return CMD_FAILURE;
+ }
+ }
+ else
+ {
+ /* Adding a filter */
+ if (parameters.size() >= 4)
+ {
+ const std::string& freeform = parameters[0];
+ FilterAction type;
+ const std::string& flags = parameters[2];
+ unsigned int reasonindex;
+ unsigned long duration = 0;
+
+ if (!ModuleFilter::StringToFilterAction(parameters[1], type))
+ {
+ if (ServerInstance->XLines->GetFactory("SHUN"))
+ user->WriteNotice("*** Invalid filter type '" + parameters[1] + "'. Supported types are 'gline', 'zline', 'none', 'warn', 'block', 'silent', 'kill', and 'shun'.");
+ else
+ user->WriteNotice("*** Invalid filter type '" + parameters[1] + "'. Supported types are 'gline', 'zline', 'none', 'warn', 'block', 'silent', and 'kill'.");
+ return CMD_FAILURE;
+ }
+
+ if (type == FA_GLINE || type == FA_ZLINE || type == FA_SHUN)
+ {
+ if (parameters.size() >= 5)
+ {
+ if (!InspIRCd::Duration(parameters[3], duration))
+ {
+ user->WriteNotice("*** Invalid duration for filter");
+ return CMD_FAILURE;
+ }
+ reasonindex = 4;
+ }
+ else
+ {
+ user->WriteNotice("*** Not enough parameters: When setting a '" + parameters[1] + "' type filter, a duration must be specified as the third parameter.");
+ return CMD_FAILURE;
+ }
+ }
+ else
+ {
+ reasonindex = 3;
+ }
+
+ Module* me = creator;
+ std::pair<bool, std::string> result = static_cast<ModuleFilter*>(me)->AddFilter(freeform, type, parameters[reasonindex], duration, flags);
+ if (result.first)
+ {
+ const std::string message = InspIRCd::Format("'%s', type '%s'%s, flags '%s', reason: %s",
+ freeform.c_str(), parameters[1].c_str(),
+ (duration ? InspIRCd::Format(", duration '%s'",
+ InspIRCd::DurationString(duration).c_str()).c_str()
+ : ""), flags.c_str(), parameters[reasonindex].c_str());
+
+ user->WriteNotice("*** Added filter " + message);
+ ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'f' : 'F',
+ "%s added filter %s", user->nick.c_str(), message.c_str());
+
+ return CMD_SUCCESS;
+ }
+ else
+ {
+ user->WriteNotice("*** Filter '" + freeform + "' could not be added: " + result.second);
+ return CMD_FAILURE;
+ }
+ }
+ else
+ {
+ user->WriteNotice("*** Not enough parameters.");
+ return CMD_FAILURE;
+ }
+
+ }
+}
+
+bool ModuleFilter::AppliesToMe(User* user, FilterResult* filter, int iflags)
+{
+ const AccountExtItem* accountext = GetAccountExtItem();
+
+ if ((filter->flag_no_opers) && user->IsOper())
+ return false;
+ if ((filter->flag_no_registered) && accountext && accountext->get(user))
+ 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()
+ : ServerProtocol::SyncEventListener(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)
+ {
+ bool is_selfmsg = false;
+ switch (msgtarget.type)
+ {
+ case 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;
+
+ if (user == t)
+ is_selfmsg = true;
+ break;
+ }
+ case MessageTarget::TYPE_CHANNEL:
+ {
+ Channel* t = msgtarget.Get<Channel>();
+ if (exemptedchans.count(t->name))
+ return MOD_RES_PASSTHRU;
+ break;
+ }
+ case MessageTarget::TYPE_SERVER:
+ return MOD_RES_PASSTHRU;
+ }
+
+ if (is_selfmsg && warnonselfmsg)
+ {
+ ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("WARNING: %s's self message matched %s (%s)",
+ user->nick.c_str(), f->freeform.c_str(), f->reason.c_str()));
+ return MOD_RES_PASSTHRU;
+ }
+ else if (f->action == FA_WARN)
+ {
+ ServerInstance->SNO->WriteGlobalSno('f', InspIRCd::Format("WARNING: %s's message to %s matched %s (%s)",
+ user->nick.c_str(), msgtarget.GetName().c_str(), f->freeform.c_str(), f->reason.c_str()));
+ return MOD_RES_PASSTHRU;
+ }
+ else 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(), msgtarget.GetName().c_str(), f->freeform.c_str(), f->reason.c_str()));
+ if (notifyuser)
+ {
+ if (msgtarget.type == MessageTarget::TYPE_CHANNEL)
+ user->WriteNumeric(Numerics::CannotSendTo(msgtarget.Get<Channel>(), InspIRCd::Format("Your message to this channel was blocked: %s.", f->reason.c_str())));
+ else
+ user->WriteNumeric(Numerics::CannotSendTo(msgtarget.Get<User>(), InspIRCd::Format("Your message to this user was blocked: %s.", f->reason.c_str())));
+ }
+ else
+ details.echo_original = true;
+ }
+ else if (f->action == FA_SILENT)
+ {
+ if (notifyuser)
+ {
+ if (msgtarget.type == MessageTarget::TYPE_CHANNEL)
+ user->WriteNumeric(Numerics::CannotSendTo(msgtarget.Get<Channel>(), InspIRCd::Format("Your message to this channel was blocked: %s.", f->reason.c_str())));
+ else
+ user->WriteNumeric(Numerics::CannotSendTo(msgtarget.Get<User>(), InspIRCd::Format("Your message to this user was blocked: %s.", f->reason.c_str())));
+ }
+ 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(), msgtarget.GetName().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(),
+ msgtarget.GetName().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(),
+ msgtarget.GetName().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(),
+ msgtarget.GetName().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 " + msgtarget.GetName() + ": " + f->reason + " Action: " + ModuleFilter::FilterActionToString(f->action));
+ return MOD_RES_DENY;