+ Module *me = creator;
+ std::pair<bool, std::string> result = static_cast<ModuleFilter *>(me)->AddFilter(freeform, type, parameters[reasonindex], duration, flags);
+ if (result.first)
+ {
+ user->WriteNotice("*** Added filter '" + freeform + "', type '" + parameters[1] + "'" +
+ (duration ? ", duration " + parameters[3] : "") + ", flags '" + flags + "', reason: '" +
+ parameters[reasonindex] + "'");
+
+ ServerInstance->SNO->WriteToSnoMask(IS_LOCAL(user) ? 'a' : 'A', "FILTER: "+user->nick+" added filter '"+freeform+"', type '"+parameters[1]+"', "+(duration ? "duration "+parameters[3]+", " : "")+"flags '"+flags+"', reason: "+parameters[reasonindex]);
+
+ 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)
+{
+ 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()
+ : initing(true), filtcommand(this), RegexEngine(this, "regex")
+{
+}
+
+void ModuleFilter::init()
+{
+ ServerInstance->Modules->AddService(filtcommand);
+ Implementation eventlist[] = { I_OnPreCommand, I_OnStats, I_OnSyncNetwork, I_OnDecodeMetaData, I_OnUserPreMessage, I_OnRehash, I_OnUnloadModule };
+ ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ OnRehash(NULL);
+}
+
+CullResult ModuleFilter::cull()
+{
+ FreeFilters();
+ return Module::cull();
+}
+
+void ModuleFilter::FreeFilters()
+{
+ for (std::vector<ImplFilter>::const_iterator i = filters.begin(); i != filters.end(); ++i)
+ delete i->regex;
+
+ filters.clear();
+}
+
+ModResult ModuleFilter::OnUserPreMessage(User* user, void* dest, int target_type, std::string& text, char status, CUList& exempt_list, MessageType msgtype)
+{
+ /* Leave ulines alone */
+ if ((ServerInstance->ULine(user->server)) || (!IS_LOCAL(user)))
+ return MOD_RES_PASSTHRU;
+
+ flags = (msgtype == MSG_PRIVMSG) ? FLAG_PRIVMSG : FLAG_NOTICE;
+
+ FilterResult* f = this->FilterMatch(user, text, flags);
+ if (f)
+ {
+ std::string target;
+ if (target_type == TYPE_USER)
+ {
+ User* t = (User*)dest;
+ target = t->nick;
+ }
+ else if (target_type == TYPE_CHANNEL)
+ {
+ Channel* t = (Channel*)dest;
+ if (exemptfromfilter.find(t->name) != exemptfromfilter.end())
+ return MOD_RES_PASSTHRU;
+
+ target = t->name;
+ }
+ if (f->action == FA_BLOCK)
+ {
+ ServerInstance->SNO->WriteGlobalSno('a', "FILTER: "+user->nick+" had their message filtered, target was "+target+": "+f->reason);
+ if (target_type == TYPE_CHANNEL)
+ user->WriteNumeric(404, "%s %s :Message to channel blocked and opers notified (%s)",user->nick.c_str(), target.c_str(), f->reason.c_str());
+ else
+ user->WriteNotice("Your message to "+target+" was blocked and opers notified: "+f->reason);
+ }
+ else if (f->action == FA_SILENT)
+ {
+ if (target_type == TYPE_CHANNEL)
+ user->WriteNumeric(404, "%s %s :Message to channel blocked (%s)",user->nick.c_str(), target.c_str(), f->reason.c_str());
+ else
+ user->WriteNotice("Your message to "+target+" was blocked: "+f->reason);
+ }
+ else if (f->action == FA_KILL)
+ {
+ ServerInstance->Users->QuitUser(user, "Filtered: " + f->reason);
+ }
+ else if (f->action == FA_GLINE)
+ {
+ GLine* gl = new GLine(ServerInstance->Time(), f->gline_time, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), "*", user->GetIPString());
+ if (ServerInstance->XLines->AddLine(gl,NULL))
+ {
+ ServerInstance->XLines->ApplyLines();
+ }
+ else
+ delete gl;
+ }
+
+ ServerInstance->Logs->Log("FILTER", LOG_DEFAULT, "FILTER: "+ user->nick + " had their message filtered, target was " + target + ": " + f->reason + " Action: " + ModuleFilter::FilterActionToString(f->action));
+ return MOD_RES_DENY;
+ }
+ return MOD_RES_PASSTHRU;
+}
+
+ModResult ModuleFilter::OnPreCommand(std::string &command, std::vector<std::string> ¶meters, LocalUser *user, bool validated, const std::string &original_line)
+{
+ if (validated && IS_LOCAL(user))
+ {
+ flags = 0;
+ bool parting;
+
+ if (command == "QUIT")
+ {
+ /* 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 (exemptfromfilter.find(parameters[0]) != exemptfromfilter.end())
+ 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 blocking, OR theyre quitting and its a KILL action
+ * (we cant kill someone whos already quitting, so filter them anyway)
+ */
+ if ((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 gline *@IP so that if their host doesnt resolve the gline still applies. */
+ GLine* gl = new GLine(ServerInstance->Time(), f->gline_time, ServerInstance->Config->ServerName.c_str(), f->reason.c_str(), "*", user->GetIPString());
+ if (ServerInstance->XLines->AddLine(gl,NULL))