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