+ /* QUIT with no reason: nothing to do */
+ if (parameters.size() < 1)
+ return MOD_RES_PASSTHRU;
+
+ parting = false;
+ flags = FLAG_QUIT;
+ }
+ else if (command == "PART")
+ {
+ /* PART with no reason: nothing to do */
+ if (parameters.size() < 2)
+ return MOD_RES_PASSTHRU;
+
+ if (exemptedchans.count(parameters[0]))
+ return MOD_RES_PASSTHRU;
+
+ parting = true;
+ flags = FLAG_PART;
+ }
+ else
+ /* We're only messing with PART and QUIT */
+ return MOD_RES_PASSTHRU;
+
+ FilterResult* f = this->FilterMatch(user, parameters[parting ? 1 : 0], flags);
+ if (!f)
+ /* PART or QUIT reason doesnt match a filter */
+ return MOD_RES_PASSTHRU;
+
+ /* We cant block a part or quit, so instead we change the reason to 'Reason filtered' */
+ parameters[parting ? 1 : 0] = "Reason filtered";
+
+ /* We're warning or blocking, OR they're quitting and its a KILL action
+ * (we cant kill someone whos already quitting, so filter them anyway)
+ */
+ if ((f->action == FA_WARN) || (f->action == FA_BLOCK) || (((!parting) && (f->action == FA_KILL))) || (f->action == FA_SILENT))
+ {
+ return MOD_RES_PASSTHRU;
+ }
+ else
+ {
+ /* Are they parting, if so, kill is applicable */
+ if ((parting) && (f->action == FA_KILL))
+ {
+ user->WriteNotice("*** Your PART message was filtered: " + f->reason);
+ ServerInstance->Users->QuitUser(user, "Filtered: " + f->reason);
+ }
+ if (f->action == FA_GLINE)
+ {
+ /* Note: We G-line *@IP so that if their host doesn't resolve the G-line still applies. */
+ 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 %s message 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(),
+ command.c_str(), f->freeform.c_str(), f->reason.c_str()));
+
+ if (ServerInstance->XLines->AddLine(gl,NULL))
+ {
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ delete gl;
+ }
+ 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 %s message 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(),
+ command.c_str(), f->freeform.c_str(), f->reason.c_str()));
+
+ if (ServerInstance->XLines->AddLine(zl,NULL))
+ {
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ delete zl;
+ }
+ else if (f->action == FA_SHUN && (ServerInstance->XLines->GetFactory("SHUN")))
+ {
+ /* Note: We shun *!*@IP so that if their host doesnt resolve the shun still applies. */
+ 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 %s message 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(),
+ command.c_str(), f->freeform.c_str(), f->reason.c_str()));
+
+ if (ServerInstance->XLines->AddLine(sh, NULL))
+ {
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ delete sh;
+ }
+ return MOD_RES_DENY;
+ }
+ }
+ return MOD_RES_PASSTHRU;
+}
+
+void ModuleFilter::ReadConfig(ConfigStatus& status)
+{
+ ConfigTagList tags = ServerInstance->Config->ConfTags("exemptfromfilter");
+ exemptedchans.clear();
+ exemptednicks.clear();
+
+ for (ConfigIter i = tags.first; i != tags.second; ++i)
+ {
+ ConfigTag* tag = i->second;
+
+ // If "target" is not found, try the old "channel" key to keep compatibility with 2.0 configs
+ const std::string target = tag->getString("target", tag->getString("channel"), 1);
+ if (!target.empty())
+ {
+ if (target[0] == '#')
+ exemptedchans.insert(target);
+ else
+ exemptednicks.insert(target);