]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_silence.cpp
2a8fbae94319502e98b51c1e3aad7a00772b580b
[user/henk/code/inspircd.git] / src / modules / m_silence.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "wildcard.h"
16
17 /* $ModDesc: Provides support for the /SILENCE command */
18
19 /* Improved drop-in replacement for the /SILENCE command
20  * syntax: /SILENCE [+|-]<mask> <p|c|i|n|t|a|x> as in <privatemessage|channelmessage|invites|privatenotice|channelnotice|all|exclude>
21  *
22  * example that blocks all except private messages
23  *  /SILENCE +*!*@* a
24  *  /SILENCE +*!*@* px
25  *
26  * example that blocks all invites except from channel services
27  *  /SILENCE +*!*@* i
28  *  /SILENCE +chanserv!services@chatters.net ix
29  *
30  * example that blocks some bad dude from private, notice and inviting you
31  *  /SILENCE +*!kiddie@lamerz.net pin
32  *
33  * TODO: possibly have add and remove check for existing host and only modify flags according to
34  *       what's been changed instead of having to remove first, then add if you want to change
35  *       an entry.
36  */
37
38 // pair of hostmask and flags
39 typedef std::pair<std::string, int> silenceset;
40
41 // deque list of pairs
42 typedef std::deque<silenceset> silencelist;
43
44 // intmasks for flags
45 static int SILENCE_PRIVATE      = 0x0001; /* p  private messages      */
46 static int SILENCE_CHANNEL      = 0x0002; /* c  channel messages      */
47 static int SILENCE_INVITE       = 0x0004; /* i  invites               */
48 static int SILENCE_NOTICE       = 0x0008; /* n  notices               */
49 static int SILENCE_CNOTICE      = 0x0010; /* t  channel notices       */
50 static int SILENCE_ALL          = 0x0020; /* a  all, (pcint)          */
51 static int SILENCE_EXCLUDE      = 0x0040; /* x  exclude this pattern  */
52
53
54 class CommandSVSSilence : public Command
55 {
56  public:
57         CommandSVSSilence(InspIRCd* Instance) : Command(Instance,"SVSSILENCE", 0, 2)
58         {
59                 this->source = "m_silence.so";
60                 syntax = "<target> {[+|-]<mask> <p|c|i|n|t|a|x>}";
61                 TRANSLATE3(TR_NICK, TR_TEXT, TR_END); /* we watch for a nick. not a UID. */
62         }
63
64         CmdResult Handle (const char* const* parameters, int pcnt, User *user)
65         {
66                 /*
67                  * XXX: thought occurs to me
68                  * We may want to change the syntax of this command to
69                  * SVSSILENCE <flagsora+> +<nick> -<nick> +<nick>
70                  * style command so services can modify lots of entries at once.
71                  * leaving it backwards compatible for now as it's late. -- w
72                  */
73                 if (!ServerInstance->ULine(user->server))
74                         return CMD_FAILURE;
75                         
76                 User *u = ServerInstance->FindNick(parameters[0]);
77                 if (!u)
78                         return CMD_FAILURE;
79                         
80                 if (IS_LOCAL(u))
81                 {
82                         ServerInstance->Parser->CallHandler("SILENCE", &parameters[1], 1, u);
83                 }
84                 
85                 return CMD_SUCCESS;
86         }
87 };
88
89 class CommandSilence : public Command
90 {
91         unsigned int& maxsilence;
92  public:
93         CommandSilence (InspIRCd* Instance, unsigned int &max) : Command(Instance,"SILENCE", 0, 0), maxsilence(max)
94         {
95                 this->source = "m_silence_ext.so";
96                 syntax = "{[+|-]<mask> <p|c|i|n|t|a|x>}";
97                 TRANSLATE3(TR_TEXT, TR_TEXT, TR_END);
98         }
99
100         CmdResult Handle (const char* const* parameters, int pcnt, User *user)
101         {
102                 if (!pcnt)
103                 {
104                         // no parameters, show the current silence list.
105                         // Use Extensible::GetExt to fetch the silence list
106                         silencelist* sl;
107                         user->GetExt("silence_list", sl);
108                         // if the user has a silence list associated with their user record, show it
109                         if (sl)
110                         {
111                                 for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
112                                 {
113                                         user->WriteNumeric(271, "%s %s %s %s",user->nick, user->nick,c->first.c_str(), DecompPattern(c->second).c_str());
114                                 }
115                         }
116                         user->WriteNumeric(272, "%s :End of Silence List",user->nick);
117
118                         return CMD_LOCALONLY;
119                 }
120                 else if (pcnt > 0)
121                 {
122                         // one or more parameters, add or delete entry from the list (only the first parameter is used)
123                         std::string mask = parameters[0] + 1;
124                         char action = *parameters[0];
125                         // Default is private and notice so clients do not break
126                         int pattern = CompilePattern("pn");
127
128                         // if pattern supplied, use it
129                         if (pcnt > 1) {
130                                 pattern = CompilePattern(parameters[1]);
131                         }
132                         
133                         if (!mask.length())
134                         {
135                                 // 'SILENCE +' or 'SILENCE -', assume *!*@*
136                                 mask = "*!*@*";
137                         }
138                         
139                         ModeParser::CleanMask(mask);
140
141                         if (action == '-')
142                         {
143                                 // fetch their silence list
144                                 silencelist* sl;
145                                 user->GetExt("silence_list", sl);
146                                 // does it contain any entries and does it exist?
147                                 if (sl)
148                                 {
149                                         for (silencelist::iterator i = sl->begin(); i != sl->end(); i++)
150                                         {
151                                                 // search through for the item
152                                                 irc::string listitem = i->first.c_str();
153                                                 if (listitem == mask && i->second == pattern)
154                                                 {
155                                                         sl->erase(i);
156                                                         user->WriteNumeric(950, "%s %s :Removed %s %s from silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
157                                                         if (!sl->size())
158                                                         {
159                                                                 delete sl;
160                                                                 user->Shrink("silence_list");
161                                                         }
162                                                         return CMD_SUCCESS;
163                                                 }
164                                         }
165                                 }
166                                 user->WriteNumeric(952, "%s %s :%s %s does not exist on your silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
167                         }
168                         else if (action == '+')
169                         {
170                                 // fetch the user's current silence list
171                                 silencelist* sl;
172                                 user->GetExt("silence_list", sl);
173                                 // what, they dont have one??? WE'RE ALL GONNA DIE! ...no, we just create an empty one.
174                                 if (!sl)
175                                 {
176                                         sl = new silencelist;
177                                         user->Extend("silence_list", sl);
178                                 }
179                                 if (sl->size() > maxsilence)
180                                 {
181                                         user->WriteNumeric(952, "%s %s :Your silence list is full",user->nick, user->nick);
182                                         return CMD_FAILURE;
183                                 }
184                                 for (silencelist::iterator n = sl->begin(); n != sl->end();  n++)
185                                 {
186                                         irc::string listitem = n->first.c_str();
187                                         if (listitem == mask && n->second == pattern)
188                                         {
189                                                 user->WriteNumeric(952, "%s %s :%s %s is already on your silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
190                                                 return CMD_FAILURE;
191                                         }
192                                 }
193                                 if (((pattern & SILENCE_EXCLUDE) > 0))
194                                 {
195                                         sl->push_front(silenceset(mask,pattern));
196                                 }
197                                 else
198                                 {
199                                         sl->push_back(silenceset(mask,pattern));
200                                 }
201                                 user->WriteNumeric(951, "%s %s :Added %s %s to silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
202                                 return CMD_SUCCESS;
203                         }
204                 }
205                 return CMD_LOCALONLY;
206         }
207
208         /* turn the nice human readable pattern into a mask */
209         int CompilePattern(const char* pattern)
210         {
211                 int p = 0;
212                 for (const char* n = pattern; *n; n++)
213                 {
214                         switch (*n)
215                         {
216                                 case 'p':
217                                         p |= SILENCE_PRIVATE;
218                                         break;
219                                 case 'c':
220                                         p |= SILENCE_CHANNEL;
221                                         break;
222                                 case 'i': 
223                                         p |= SILENCE_INVITE;
224                                         break;
225                                 case 'n':
226                                         p |= SILENCE_NOTICE;
227                                         break;
228                                 case 't':
229                                         p |= SILENCE_CNOTICE;
230                                         break;
231                                 case 'a':
232                                 case '*':
233                                         p |= SILENCE_ALL;
234                                         break;
235                                 case 'x':
236                                         p |= SILENCE_EXCLUDE;
237                                         break;
238                                 default:
239                                         break;
240                         }
241                 }
242                 return p;
243         }
244
245         /* turn the mask into a nice human readable format */
246         std::string DecompPattern (const int pattern)
247         {
248                 std::string out;
249                 if ((pattern & SILENCE_PRIVATE) > 0)
250                         out += ",privatemessages";
251                 if ((pattern & SILENCE_CHANNEL) > 0)
252                         out += ",channelmessages";
253                 if ((pattern & SILENCE_INVITE) > 0)
254                         out += ",invites";
255                 if ((pattern & SILENCE_NOTICE) > 0)
256                         out += ",privatenotices";
257                 if ((pattern & SILENCE_CNOTICE) > 0)
258                         out += ",channelnotices";
259                 if ((pattern & SILENCE_ALL) > 0)
260                         out = ",all";
261                 if ((pattern & SILENCE_EXCLUDE) > 0)
262                         out += ",exclude";
263                 return "<" + out.substr(1) + ">";
264         }
265
266 };
267
268 class ModuleSilence : public Module
269 {
270         CommandSilence* cmdsilence;
271         CommandSVSSilence *cmdsvssilence;
272         unsigned int maxsilence;
273  public:
274  
275         ModuleSilence(InspIRCd* Me)
276                 : Module(Me), maxsilence(32)
277         {
278                 OnRehash(NULL, "");
279                 cmdsilence = new CommandSilence(ServerInstance,maxsilence);
280                 cmdsvssilence = new CommandSVSSilence(ServerInstance);
281                 ServerInstance->AddCommand(cmdsilence);
282                 ServerInstance->AddCommand(cmdsvssilence);
283
284                 Implementation eventlist[] = { I_OnRehash, I_OnBuildExemptList, I_OnUserQuit, I_On005Numeric, I_OnUserPreNotice, I_OnUserPreMessage, I_OnUserPreInvite };
285                 ServerInstance->Modules->Attach(eventlist, this, 7);
286         }
287
288         virtual void OnRehash(User* user, const std::string &parameter)
289         {
290                 ConfigReader Conf(ServerInstance);
291                 maxsilence = Conf.ReadInteger("silence", "maxentries", 0, true);
292                 if (!maxsilence)
293                         maxsilence = 32;
294         }
295
296
297         virtual void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
298         {
299                 // when the user quits tidy up any silence list they might have just to keep things tidy
300                 silencelist* sl;
301                 user->GetExt("silence_list", sl);
302                 if (sl)
303                 {
304                         delete sl;
305                         user->Shrink("silence_list");
306                 }
307         }
308
309         virtual void On005Numeric(std::string &output)
310         {
311                 // we don't really have a limit...
312                 output = output + " ESILENCE SILENCE=" + ConvToStr(maxsilence);
313         }
314
315         virtual void OnBuildExemptList(MessageType message_type, Channel* chan, User* sender, char status, CUList &exempt_list, const std::string &text)
316         {
317                 int public_silence = (message_type == MSG_PRIVMSG ? SILENCE_CHANNEL : SILENCE_CNOTICE);
318                 CUList *ulist;
319                 switch (status)
320                 {
321                         case '@':
322                                 ulist = chan->GetOppedUsers();
323                                 break;
324                         case '%':
325                                 ulist = chan->GetHalfoppedUsers();
326                                 break;
327                         case '+':
328                                 ulist = chan->GetVoicedUsers();
329                                 break;
330                         default:
331                                 ulist = chan->GetUsers();
332                                 break;
333                 }
334
335                 for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
336                 {
337                         if (IS_LOCAL(i->first))
338                         {
339                                 if (MatchPattern(i->first, sender, public_silence) == 1)
340                                 {
341                                         exempt_list[i->first] = i->first->nick;
342                                 }
343                         }
344                 }
345         }
346
347         virtual int PreText(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list, int silence_type)
348         {
349                 if (!IS_LOCAL(user))
350                         return 0;
351
352                 if (target_type == TYPE_USER)
353                 {
354                         return MatchPattern((User*)dest, user, silence_type);
355                 }
356                 else if (target_type == TYPE_CHANNEL)
357                 {
358                         Channel* chan = (Channel*)dest;
359                         if (chan)
360                         {
361                                 this->OnBuildExemptList((silence_type == SILENCE_PRIVATE ? MSG_PRIVMSG : MSG_NOTICE), chan, user, status, exempt_list, "");
362                         }
363                 }
364                 return 0;
365         }
366
367         virtual int OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
368         {
369                 return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_PRIVATE);
370         }
371
372         virtual int OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
373         {
374                 return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_NOTICE);
375         }
376
377         virtual int OnUserPreInvite(User* source,User* dest,Channel* channel, time_t timeout)
378         {
379                 return MatchPattern(dest, source, SILENCE_INVITE);
380         }
381
382         int MatchPattern(User* dest, User* source, int pattern)
383         {
384                 /* Server source */
385                 if (!source || !dest)
386                         return 1;
387
388                 silencelist* sl;
389                 dest->GetExt("silence_list", sl);
390                 if (sl)
391                 {
392                         for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
393                         {
394                                 if (((((c->second & pattern) > 0)) || ((c->second & SILENCE_ALL) > 0)) && (ServerInstance->MatchText(source->GetFullHost(), c->first)))
395                                         return !(((c->second & SILENCE_EXCLUDE) > 0));
396                         }
397                 }
398                 return 0;
399         }
400
401         virtual ~ModuleSilence()
402         {
403         }
404         
405         virtual Version GetVersion()
406         {
407                 return Version(1, 2, 0, 1, VF_COMMON | VF_VENDOR, API_VERSION);
408         }
409 };
410
411 MODULE_INIT(ModuleSilence)