diff options
-rw-r--r-- | src/modules/m_silence_ext.cpp | 494 |
1 files changed, 494 insertions, 0 deletions
diff --git a/src/modules/m_silence_ext.cpp b/src/modules/m_silence_ext.cpp new file mode 100644 index 000000000..50810f7d7 --- /dev/null +++ b/src/modules/m_silence_ext.cpp @@ -0,0 +1,494 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev. + * E-mail: + * <brain@chatspike.net> + * <Craig@chatspike.net> + * + * Written by Craig Edwards, Craig McLure, and others. + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +using namespace std; + +#include <stdio.h> +#include <string> +#include <vector> +#include <stdarg.h> +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "hashcomp.h" +#include "inspircd.h" +#include "wildcard.h" + +/* $ModDesc: Provides support for the /SILENCE command */ + +/* Improved drop-in replacement for the /SILENCE command + * syntax: /SILENCE [+|-]<mask> <p|c|i|n|a|x> as in <private|channel|invites|notices|all|exclude> + * + * example that blocks all except private messages + * /SILENCE +*!*@* a + * /SILENCE +*!*@* px + * + * example that blocks all invites except from channel services + * /SILENCE +*!*@* i + * /SILENCE +chanserv!services@chatters.net ix + * + * example that blocks some bad dude from private, notice and inviting you + * /SILENCE +*!kiddie@lamerz.net pin + * + * TODO: possibly have add and remove check for existing host and only modify flags according to + * what's been changed instead of having to remove first, then add if you want to change + * an entry. + */ + +// pair of hostmask and flags +typedef std::pair<std::string, int> silenceset; + +// deque list of pairs +typedef std::deque<silenceset> silencelist; + +// intmasks for flags +static int SILENCE_PRIVATE = 0x0001; /* p private messages */ +static int SILENCE_CHANNEL = 0x0002; /* c channel messages */ +static int SILENCE_INVITE = 0x0004; /* i invites */ +static int SILENCE_NOTICE = 0x0008; /* n notices */ +static int SILENCE_ALL = 0x0010; /* a all, (pcin) */ +static int SILENCE_EXCLUDE = 0x0020; /* x exclude this pattern */ + + +class cmd_silence : public command_t +{ + public: + cmd_silence (InspIRCd* Instance) : command_t(Instance,"SILENCE", 0, 0) + { + this->source = "m_silence_ext.so"; + syntax = "{[+|-]<mask> <p|c|i|n|a|x>}"; + } + + CmdResult Handle (const char** parameters, int pcnt, userrec *user) + { + if (!pcnt) + { + // no parameters, show the current silence list. + // Use Extensible::GetExt to fetch the silence list + silencelist* sl; + user->GetExt("silence_list", sl); + // if the user has a silence list associated with their user record, show it + if (sl) + { + for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++) + { + user->WriteServ("271 %s %s %s %s",user->nick, user->nick,c->first.c_str(), DecompPattern(c->second).c_str()); + } + } + user->WriteServ("272 %s :End of Silence List",user->nick); + + return CMD_SUCCESS; + } + else if (pcnt > 0) + { + // one or more parameters, add or delete entry from the list (only the first parameter is used) + std::string mask = parameters[0] + 1; + char action = *parameters[0]; + // Default is private and notice so clients do not break + int pattern = CompilePattern("pn"); + + // if pattern supplied, use it + if (pcnt > 1) { + pattern = CompilePattern(parameters[1]); + } + + if (!mask.length()) + { + // 'SILENCE +' or 'SILENCE -', assume *!*@* + mask = "*!*@*"; + } + + ModeParser::CleanMask(mask); + + if (action == '-') + { + // fetch their silence list + silencelist* sl; + user->GetExt("silence_list", sl); + // does it contain any entries and does it exist? + if (sl) + { + if (sl->size()) + { + silencelist::iterator i,safei; + for (i = sl->begin(); i != sl->end(); i++) + { + // search through for the item + irc::string listitem = i->first.c_str(); + if (listitem == mask && i->second == pattern) + { + safei = i; + --i; + sl->erase(safei); + user->WriteServ("950 %s %s :Removed %s %s from silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str()); + break; + } + } + } + else + { + // tidy up -- if a user's list is empty, theres no use having it + // hanging around in the user record. + DELETE(sl); + user->Shrink("silence_list"); + } + } + } + else if (action == '+') + { + // fetch the user's current silence list + silencelist* sl; + user->GetExt("silence_list", sl); + // what, they dont have one??? WE'RE ALL GONNA DIE! ...no, we just create an empty one. + if (!sl) + { + sl = new silencelist; + user->Extend("silence_list", sl); + } + for (silencelist::iterator n = sl->begin(); n != sl->end(); n++) + { + irc::string listitem = n->first.c_str(); + if (listitem == mask && n->second == pattern) + { + user->WriteServ("952 %s %s :%s %s is already on your silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str()); + return CMD_SUCCESS; + } + } + if (((pattern & SILENCE_EXCLUDE) > 0)) + { + sl->push_front(silenceset(mask,pattern)); + } + else + { + sl->push_back(silenceset(mask,pattern)); + } + user->WriteServ("951 %s %s :Added %s %s to silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str()); + return CMD_SUCCESS; + } + } + return CMD_SUCCESS; + } + + /* turn the nice human readable pattern into a mask */ + int CompilePattern(const char* pattern) + { + int p = 0; + for (uint n = 0; n < strlen(pattern); n++) + { + switch (pattern[n]) + { + case 'p': + p |= SILENCE_PRIVATE; + break; + case 'c': + p |= SILENCE_CHANNEL; + break; + case 'i': + p |= SILENCE_INVITE; + break; + case 'n': + p |= SILENCE_NOTICE; + break; + case 'a': + p |= SILENCE_ALL; + break; + case 'x': + p |= SILENCE_EXCLUDE; + break; + default: + break; + } + } + return p; + } + + /* turn the mask into a nice human readable format */ + std::string DecompPattern (const int pattern) + { + std::string out = ""; + if ((pattern & SILENCE_PRIVATE) > 0) + out += ",private"; + if ((pattern & SILENCE_CHANNEL) > 0) + out += ",channel"; + if ((pattern & SILENCE_INVITE) > 0) + out += ",invites"; + if ((pattern & SILENCE_NOTICE) > 0) + out += ",notices"; + if ((pattern & SILENCE_ALL) > 0) + out = ",all"; + if ((pattern & SILENCE_EXCLUDE) > 0) + out += ",exclude"; + return "<" + out.substr(1) + ">"; + } + +}; + +class ModuleSilence : public Module +{ + + cmd_silence* mycommand; + public: + + ModuleSilence(InspIRCd* Me) + : Module::Module(Me) + { + + mycommand = new cmd_silence(ServerInstance); + ServerInstance->AddCommand(mycommand); + } + + void Implements(char* List) + { + List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = 1; + List[I_OnUserPreInvite] = 1; + List[I_OnPreCommand] = 1; + } + + virtual void OnUserQuit(userrec* user, const std::string &reason) + { + // when the user quits tidy up any silence list they might have just to keep things tidy + // and to prevent a HONKING BIG MEMORY LEAK! + silencelist* sl; + user->GetExt("silence_list", sl); + if (sl) + { + DELETE(sl); + user->Shrink("silence_list"); + } + } + + virtual void On005Numeric(std::string &output) + { + // we don't really have a limit... + output = output + " SILENCE=999"; + } + + virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status) + { + if (target_type == TYPE_USER) + { + return MatchPattern((userrec*)dest, user, SILENCE_PRIVATE); + } + return 0; + } + + virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status) + { + return MatchPattern((userrec*)dest, user, SILENCE_NOTICE); + } + + virtual int OnUserPreInvite(userrec* source,userrec* dest,chanrec* channel) + { + return MatchPattern(dest, source, SILENCE_INVITE); + } + + int MatchPattern(userrec* dest, userrec* source, int pattern) + { + silencelist* sl; + dest->GetExt("silence_list", sl); + if (sl) + { + for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++) + { + if ((match(source->GetFullHost(), c->first.c_str())) && ( ((c->second & pattern) > 0)) || ((c->second & SILENCE_ALL) > 0)) + { + if (((c->second & SILENCE_EXCLUDE) > 0)) + { + return 0; + } + else { + return 1; + } + } + } + } + return 0; + } + + virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line) + { + /* Implement the part of cmd_privmsg.cpp that handles *channel* messages, if cmd_privmsg.cpp + * is changed this probably needs updating too. Also implement the actual write to the users + * on the channel. This code is from channels.cpp, and should also be changed if channels.cpp + * updates it's corresponding code + */ + if ((validated) && (command == "PRIVMSG")) + { + char status = 0; + if ((*parameters[0] == '@') || (*parameters[0] == '%') || (*parameters[0] == '+')) + { + status = *parameters[0]; + parameters[0]++; + } + if (parameters[0][0] == '#') + { + chanrec *chan; + user->idle_lastmsg = ServerInstance->Time(); + chan = ServerInstance->FindChan(parameters[0]); + if (chan) + { + if (IS_LOCAL(user)) + { + if ((chan->modes[CM_NOEXTERNAL]) && (!chan->HasUser(user))) + { + user->WriteServ("404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name); + return CMD_FAILURE; + } + if ((chan->modes[CM_MODERATED]) && (chan->GetStatus(user) < STATUS_VOICE)) + { + user->WriteServ("404 %s %s :Cannot send to channel (+m)", user->nick, chan->name); + return CMD_FAILURE; + } + } + int MOD_RESULT = 0; + + std::string temp = parameters[1]; + FOREACH_RESULT(I_OnUserPreMessage,OnUserPreMessage(user,chan,TYPE_CHANNEL,temp,status)); + if (MOD_RESULT) { + return CMD_FAILURE; + } + parameters[1] = temp.c_str(); + + if (temp == "") + { + user->WriteServ("412 %s No text to send", user->nick); + return CMD_FAILURE; + } + + /* This next call into channel.cpp is the one that gets replaced by our modified method + * chan->WriteAllExceptSender(user, false, status, "PRIVMSG %s :%s", chan->name, parameters[1]); + */ + WriteAllExceptSenderAndSilenced(chan, user, false, status, "PRIVMSG %s :%s", chan->name, parameters[1]); + + FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,chan,TYPE_CHANNEL,parameters[1],status)); + return 1; + } + else + { + /* no such nick/channel */ + user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]); + return 1; + } + return 1; + } + else + { + command_t* privmsg_command = ServerInstance->Parser->GetHandler("PRIVMSG"); + if (privmsg_command) + { + privmsg_command->Handle(parameters, pcnt, user); + return 1; + } + else + { + ServerInstance->Log(DEBUG, "Could not find PRIVMSG Command!"); + } + } + } + return 0; + } + + /* Taken from channels.cpp and slightly modified, see OnPreCommand above*/ + void WriteAllExceptSenderAndSilenced(chanrec* chan, userrec* user, bool serversource, char status, char* text, ...) + { + char textbuffer[MAXBUF]; + va_list argsPtr; + + if (!text) + return; + + va_start(argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + this->WriteAllExceptSenderAndSilenced(chan, user, serversource, status, std::string(textbuffer)); + } + + /* Taken from channels.cpp and slightly modified, see OnPreCommand above*/ + void WriteAllExceptSenderAndSilenced(chanrec* chan, userrec* user, bool serversource, char status, const std::string& text) + { + CUList *ulist; + + switch (status) + { + case '@': + ulist = chan->GetOppedUsers(); + break; + case '%': + ulist = chan->GetHalfoppedUsers(); + break; + case '+': + ulist = chan->GetVoicedUsers(); + break; + default: + ulist = chan->GetUsers(); + break; + } + + for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) + { + if ((IS_LOCAL(i->second)) && (user != i->second)) + { + if (serversource) + { + i->second->WriteServ(text); + } + else + { + if (MatchPattern(i->second, user, SILENCE_CHANNEL) == 0) + { + i->second->WriteFrom(user,text); + } + } + } + } + } + + virtual ~ModuleSilence() + { + } + + virtual Version GetVersion() + { + return Version(1,1,0,1,VF_VENDOR,API_VERSION); + } +}; + + +class ModuleSilenceFactory : public ModuleFactory +{ + public: + ModuleSilenceFactory() + { + } + + ~ModuleSilenceFactory() + { + } + + virtual Module * CreateModule(InspIRCd* Me) + { + return new ModuleSilence(Me); + } + +}; + + +extern "C" void * init_module( void ) +{ + return new ModuleSilenceFactory; +} + |