1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
7 * <brain@chatspike.net>
8 * <Craig@chatspike.net>
10 * Written by Craig Edwards, Craig McLure, and others.
11 * This program is free but copyrighted software; see
12 * the file COPYING for details.
14 * ---------------------------------------------------
30 /* $ModDesc: Provides support for the /SILENCE command */
32 /* Improved drop-in replacement for the /SILENCE command
33 * syntax: /SILENCE [+|-]<mask> <p|c|i|n|a|x> as in <private|channel|invites|notices|all|exclude>
35 * example that blocks all except private messages
39 * example that blocks all invites except from channel services
41 * /SILENCE +chanserv!services@chatters.net ix
43 * example that blocks some bad dude from private, notice and inviting you
44 * /SILENCE +*!kiddie@lamerz.net pin
46 * TODO: possibly have add and remove check for existing host and only modify flags according to
47 * what's been changed instead of having to remove first, then add if you want to change
51 // pair of hostmask and flags
52 typedef std::pair<std::string, int> silenceset;
54 // deque list of pairs
55 typedef std::deque<silenceset> silencelist;
58 static int SILENCE_PRIVATE = 0x0001; /* p private messages */
59 static int SILENCE_CHANNEL = 0x0002; /* c channel messages */
60 static int SILENCE_INVITE = 0x0004; /* i invites */
61 static int SILENCE_NOTICE = 0x0008; /* n notices */
62 static int SILENCE_ALL = 0x0010; /* a all, (pcin) */
63 static int SILENCE_EXCLUDE = 0x0020; /* x exclude this pattern */
66 class cmd_silence : public command_t
69 cmd_silence (InspIRCd* Instance) : command_t(Instance,"SILENCE", 0, 0)
71 this->source = "m_silence_ext.so";
72 syntax = "{[+|-]<mask> <p|c|i|n|a|x>}";
75 CmdResult Handle (const char** parameters, int pcnt, userrec *user)
79 // no parameters, show the current silence list.
80 // Use Extensible::GetExt to fetch the silence list
82 user->GetExt("silence_list", sl);
83 // if the user has a silence list associated with their user record, show it
86 for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
88 user->WriteServ("271 %s %s %s %s",user->nick, user->nick,c->first.c_str(), DecompPattern(c->second).c_str());
91 user->WriteServ("272 %s :End of Silence List",user->nick);
97 // one or more parameters, add or delete entry from the list (only the first parameter is used)
98 std::string mask = parameters[0] + 1;
99 char action = *parameters[0];
100 // Default is private and notice so clients do not break
101 int pattern = CompilePattern("pn");
103 // if pattern supplied, use it
105 pattern = CompilePattern(parameters[1]);
110 // 'SILENCE +' or 'SILENCE -', assume *!*@*
114 ModeParser::CleanMask(mask);
118 // fetch their silence list
120 user->GetExt("silence_list", sl);
121 // does it contain any entries and does it exist?
126 silencelist::iterator i,safei;
127 for (i = sl->begin(); i != sl->end(); i++)
129 // search through for the item
130 irc::string listitem = i->first.c_str();
131 if (listitem == mask && i->second == pattern)
136 user->WriteServ("950 %s %s :Removed %s %s from silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
143 // tidy up -- if a user's list is empty, theres no use having it
144 // hanging around in the user record.
146 user->Shrink("silence_list");
150 else if (action == '+')
152 // fetch the user's current silence list
154 user->GetExt("silence_list", sl);
155 // what, they dont have one??? WE'RE ALL GONNA DIE! ...no, we just create an empty one.
158 sl = new silencelist;
159 user->Extend("silence_list", sl);
161 for (silencelist::iterator n = sl->begin(); n != sl->end(); n++)
163 irc::string listitem = n->first.c_str();
164 if (listitem == mask && n->second == pattern)
166 user->WriteServ("952 %s %s :%s %s is already on your silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
170 if (((pattern & SILENCE_EXCLUDE) > 0))
172 sl->push_front(silenceset(mask,pattern));
176 sl->push_back(silenceset(mask,pattern));
178 user->WriteServ("951 %s %s :Added %s %s to silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
185 /* turn the nice human readable pattern into a mask */
186 int CompilePattern(const char* pattern)
189 for (uint n = 0; n < strlen(pattern); n++)
194 p |= SILENCE_PRIVATE;
197 p |= SILENCE_CHANNEL;
209 p |= SILENCE_EXCLUDE;
218 /* turn the mask into a nice human readable format */
219 std::string DecompPattern (const int pattern)
221 std::string out = "";
222 if ((pattern & SILENCE_PRIVATE) > 0)
224 if ((pattern & SILENCE_CHANNEL) > 0)
226 if ((pattern & SILENCE_INVITE) > 0)
228 if ((pattern & SILENCE_NOTICE) > 0)
230 if ((pattern & SILENCE_ALL) > 0)
232 if ((pattern & SILENCE_EXCLUDE) > 0)
234 return "<" + out.substr(1) + ">";
239 class ModuleSilence : public Module
242 cmd_silence* mycommand;
245 ModuleSilence(InspIRCd* Me)
249 mycommand = new cmd_silence(ServerInstance);
250 ServerInstance->AddCommand(mycommand);
253 void Implements(char* List)
255 List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = 1;
256 List[I_OnUserPreInvite] = 1;
257 List[I_OnPreCommand] = 1;
260 virtual void OnUserQuit(userrec* user, const std::string &reason)
262 // when the user quits tidy up any silence list they might have just to keep things tidy
263 // and to prevent a HONKING BIG MEMORY LEAK!
265 user->GetExt("silence_list", sl);
269 user->Shrink("silence_list");
273 virtual void On005Numeric(std::string &output)
275 // we don't really have a limit...
276 output = output + " ESILENCE SILENCE=999";
279 virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status)
281 if (target_type == TYPE_USER)
283 return MatchPattern((userrec*)dest, user, SILENCE_PRIVATE);
288 virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status)
290 return MatchPattern((userrec*)dest, user, SILENCE_NOTICE);
293 virtual int OnUserPreInvite(userrec* source,userrec* dest,chanrec* channel)
295 return MatchPattern(dest, source, SILENCE_INVITE);
298 int MatchPattern(userrec* dest, userrec* source, int pattern)
301 dest->GetExt("silence_list", sl);
304 for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
306 if ((match(source->GetFullHost(), c->first.c_str())) && ( ((c->second & pattern) > 0)) || ((c->second & SILENCE_ALL) > 0))
308 if (((c->second & SILENCE_EXCLUDE) > 0))
321 virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
323 /* Implement the part of cmd_privmsg.cpp that handles *channel* messages, if cmd_privmsg.cpp
324 * is changed this probably needs updating too. Also implement the actual write to the users
325 * on the channel. This code is from channels.cpp, and should also be changed if channels.cpp
326 * updates it's corresponding code
328 if ((validated) && (command == "PRIVMSG"))
331 if ((*parameters[0] == '@') || (*parameters[0] == '%') || (*parameters[0] == '+'))
333 status = *parameters[0];
336 if (parameters[0][0] == '#')
339 user->idle_lastmsg = ServerInstance->Time();
340 chan = ServerInstance->FindChan(parameters[0]);
345 if ((chan->modes[CM_NOEXTERNAL]) && (!chan->HasUser(user)))
347 user->WriteServ("404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name);
350 if ((chan->modes[CM_MODERATED]) && (chan->GetStatus(user) < STATUS_VOICE))
352 user->WriteServ("404 %s %s :Cannot send to channel (+m)", user->nick, chan->name);
358 std::string temp = parameters[1];
359 FOREACH_RESULT(I_OnUserPreMessage,OnUserPreMessage(user,chan,TYPE_CHANNEL,temp,status));
363 parameters[1] = temp.c_str();
367 user->WriteServ("412 %s No text to send", user->nick);
371 /* This next call into channel.cpp is the one that gets replaced by our modified method
372 * chan->WriteAllExceptSender(user, false, status, "PRIVMSG %s :%s", chan->name, parameters[1]);
374 WriteAllExceptSenderAndSilenced(chan, user, false, status, "PRIVMSG %s :%s", chan->name, parameters[1]);
376 FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,chan,TYPE_CHANNEL,parameters[1],status));
381 /* no such nick/channel */
382 user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);
389 command_t* privmsg_command = ServerInstance->Parser->GetHandler("PRIVMSG");
392 privmsg_command->Handle(parameters, pcnt, user);
397 ServerInstance->Log(DEBUG, "Could not find PRIVMSG Command!");
404 /* Taken from channels.cpp and slightly modified, see OnPreCommand above*/
405 void WriteAllExceptSenderAndSilenced(chanrec* chan, userrec* user, bool serversource, char status, char* text, ...)
407 char textbuffer[MAXBUF];
413 va_start(argsPtr, text);
414 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
417 this->WriteAllExceptSenderAndSilenced(chan, user, serversource, status, std::string(textbuffer));
420 /* Taken from channels.cpp and slightly modified, see OnPreCommand above*/
421 void WriteAllExceptSenderAndSilenced(chanrec* chan, userrec* user, bool serversource, char status, const std::string& text)
428 ulist = chan->GetOppedUsers();
431 ulist = chan->GetHalfoppedUsers();
434 ulist = chan->GetVoicedUsers();
437 ulist = chan->GetUsers();
441 for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
443 if ((IS_LOCAL(i->second)) && (user != i->second))
447 i->second->WriteServ(text);
451 if (MatchPattern(i->second, user, SILENCE_CHANNEL) == 0)
453 i->second->WriteFrom(user,text);
460 virtual ~ModuleSilence()
464 virtual Version GetVersion()
466 return Version(1,1,0,1,VF_VENDOR,API_VERSION);
471 class ModuleSilenceFactory : public ModuleFactory
474 ModuleSilenceFactory()
478 ~ModuleSilenceFactory()
482 virtual Module * CreateModule(InspIRCd* Me)
484 return new ModuleSilence(Me);
490 extern "C" void * init_module( void )
492 return new ModuleSilenceFactory;