]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_filter.h
f7d3438bda7e364ebbbb0586349098111ae9213d
[user/henk/code/inspircd.git] / src / modules / m_filter.h
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 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 "xline.h"
15
16 class FilterResult : public classbase
17 {
18  public:
19         std::string freeform;
20         std::string reason;
21         std::string action;
22         long gline_time;
23         bool opers_exempt;
24
25         FilterResult(const std::string free, const std::string &rea, const std::string &act, long gt, bool operex = false) : freeform(free), reason(rea),
26                                                                         action(act), gline_time(gt), opers_exempt(operex)
27         {
28         }
29
30         FilterResult()
31         {
32         }
33
34         virtual ~FilterResult()
35         {
36         }
37 };
38
39 class cmd_filter;
40
41 class FilterBase : public Module
42 {
43         cmd_filter* filtcommand;
44  public:
45         FilterBase(InspIRCd* Me, const std::string &source);
46         virtual ~FilterBase();
47         virtual void Implements(char* List);
48         virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list);
49         virtual FilterResult* FilterMatch(const std::string &text) = 0;
50         virtual bool DeleteFilter(const std::string &freeform) = 0;
51         virtual void SyncFilters(Module* proto, void* opaque) = 0;
52         virtual void SendFilter(Module* proto, void* opaque, FilterResult* iter);
53         virtual std::pair<bool, std::string> AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, bool operexempt) = 0;
54         virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list);
55         virtual void OnRehash(userrec* user, const std::string &parameter);
56         virtual Version GetVersion();
57         std::string EncodeFilter(FilterResult* filter);
58         FilterResult DecodeFilter(const std::string &data);
59         virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable = false);
60         virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata);
61         virtual int OnStats(char symbol, userrec* user, string_list &results) = 0;
62         virtual int OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line);
63 };
64
65 class cmd_filter : public command_t
66 {
67         FilterBase* Base;
68  public:
69         cmd_filter(FilterBase* f, InspIRCd* Me, const std::string &source) : command_t(Me, "FILTER", 'o', 1), Base(f)
70         {
71                 this->source = source;
72                 this->syntax = "<filter-definition> <type> <flags> [<gline-duration>] :<reason>";
73         }
74
75         CmdResult Handle(const char** parameters, int pcnt, userrec *user)
76         {
77                 if (pcnt == 1)
78                 {
79                         /* Deleting a filter */
80                         if (Base->DeleteFilter(parameters[0]))
81                         {
82                                 user->WriteServ("NOTICE %s :*** Deleted filter '%s'", user->nick, parameters[0]);
83                                 return CMD_SUCCESS;
84                         }
85                         else
86                         {
87                                 user->WriteServ("NOTICE %s :*** Filter '%s' not found on list.", user->nick, parameters[0]);
88                                 return CMD_FAILURE;
89                         }
90                 }
91                 else
92                 {
93                         /* Adding a filter */
94                         if (pcnt >= 4)
95                         {
96                                 std::string freeform = parameters[0];
97                                 std::string type = parameters[1];
98                                 std::string flags = parameters[2];
99                                 std::string reason;
100                                 long duration = 0;
101                                 bool operexempt = false;
102
103                                 for (std::string::const_iterator n = flags.begin(); n != flags.end(); ++n)
104                                 {
105                                         switch (*n)
106                                         {
107                                                 case 'o':
108                                                         operexempt = true;
109                                                 break;
110                                         }
111                                 }
112
113                                 if ((type != "gline") && (type != "none") && (type != "block") && (type != "kill") && (type != "silent"))
114                                 {
115                                         user->WriteServ("NOTICE %s :*** Invalid filter type '%s'. Supported types are 'gline', 'none', 'block', 'silent' and 'kill'.", user->nick, freeform.c_str());
116                                         return CMD_FAILURE;
117                                 }
118
119                                 if (type == "gline")
120                                 {
121                                         if (pcnt >= 5)
122                                         {
123                                                 duration = ServerInstance->Duration(parameters[3]);
124                                                 reason = parameters[4];
125                                         }
126                                         else
127                                         {
128                                                 this->TooFewParams(user, " When setting a gline type filter, a gline duration must be specified as the third parameter.");
129                                                 return CMD_FAILURE;
130                                         }
131                                 }
132                                 else
133                                 {
134                                         reason = parameters[3];
135                                 }
136                                 std::pair<bool, std::string> result = Base->AddFilter(freeform, type, reason, duration, operexempt);
137                                 if (result.first)
138                                 {
139                                         user->WriteServ("NOTICE %s :*** Added filter '%s', type '%s'%s%s, reason: '%s'", user->nick, freeform.c_str(),
140                                                         type.c_str(), (duration ? " duration: " : ""), (duration ? parameters[3] : ""),
141                                                         reason.c_str());
142                                         return CMD_SUCCESS;
143                                 }
144                                 else
145                                 {
146                                         user->WriteServ("NOTICE %s :*** Filter '%s' could not be added: %s", user->nick, freeform.c_str(), result.second.c_str());
147                                         return CMD_FAILURE;
148                                 }
149                         }
150                         else
151                         {
152                                 this->TooFewParams(user, ".");
153                                 return CMD_FAILURE;
154                         }
155
156                 }
157         }
158
159         void TooFewParams(userrec* user, const std::string &extra_text)
160         {
161                 user->WriteServ("NOTICE %s :*** Not enough parameters%s", user->nick, extra_text.c_str());
162         }
163 };
164
165 FilterBase::FilterBase(InspIRCd* Me, const std::string &source) : Module(Me)
166 {
167         filtcommand = new cmd_filter(this, Me, source);
168         ServerInstance->AddCommand(filtcommand);
169 }
170
171 FilterBase::~FilterBase()
172 {
173 }
174
175 void FilterBase::Implements(char* List)
176 {
177         List[I_OnPreCommand] = List[I_OnStats] = List[I_OnSyncOtherMetaData] = List[I_OnDecodeMetaData] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnRehash] = 1;
178 }
179
180 int FilterBase::OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
181 {
182         return OnUserPreNotice(user,dest,target_type,text,status,exempt_list);
183 }
184
185 int FilterBase::OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
186 {
187         /* Leave ulines alone */
188         if ((ServerInstance->ULine(user->server)) || (!IS_LOCAL(user)))
189                 return 0;
190
191         FilterResult* f = this->FilterMatch(text);
192         if (f)
193         {
194                 std::string target = "";
195                 if (target_type == TYPE_USER)
196                 {
197                         userrec* t = (userrec*)dest;
198                         target = std::string(t->nick);
199                 }
200                 else if (target_type == TYPE_CHANNEL)
201                 {
202                         chanrec* t = (chanrec*)dest;
203                         target = std::string(t->name);
204                 }
205                 if (f->action == "block")
206                 {       
207                         ServerInstance->WriteOpers(std::string("FILTER: ")+user->nick+" had their message filtered, target was "+target+": "+f->reason);
208                         user->WriteServ("NOTICE "+std::string(user->nick)+" :Your message has been filtered and opers notified: "+f->reason);
209                 }
210                 if (f->action == "silent")
211                 {
212                         user->WriteServ("NOTICE "+std::string(user->nick)+" :Your message has been filtered: "+f->reason);
213                 }
214                 if (f->action == "kill")
215                 {
216                         userrec::QuitUser(ServerInstance,user,"Filtered: "+f->reason);
217                 }
218                 if (f->action == "gline")
219                 {
220                         if (ServerInstance->XLines->add_gline(f->gline_time, ServerInstance->Config->ServerName, f->reason.c_str(), user->MakeHostIP()))
221                         {
222                                 ServerInstance->XLines->apply_lines(APPLY_GLINES);
223                                 FOREACH_MOD(I_OnAddGLine,OnAddGLine(f->gline_time, NULL, f->reason, user->MakeHostIP()));
224                         }
225                 }
226
227                 ServerInstance->Log(DEFAULT,"FILTER: "+std::string(user->nick)+std::string(" had their message filtered, target was ")+target+": "+f->reason+" Action: "+f->action);
228                 return 1;
229         }
230         return 0;
231 }
232
233 int FilterBase::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
234 {
235         if ((validated == 1) && (IS_LOCAL(user)))
236         {
237                 std::string checkline;
238                 int replacepoint = 0;
239                 bool parting = false;
240         
241                 if (command == "QUIT")
242                 {
243                         /* QUIT with no reason: nothing to do */
244                         if (pcnt < 1)
245                                 return 0;
246
247                         checkline = parameters[0];
248                         replacepoint = 0;
249                         parting = false;
250                 }
251                 else if (command == "PART")
252                 {
253                         /* PART with no reason: nothing to do */
254                         if (pcnt < 2)
255                                 return 0;
256
257                         checkline = parameters[1];
258                         replacepoint = 1;
259                         parting = true;
260                 }
261                 else
262                         /* We're only messing with PART and QUIT */
263                         return 0;
264
265                 FilterResult* f = this->FilterMatch(checkline);
266
267                 if (!f)
268                         /* PART or QUIT reason doesnt match a filter */
269                         return 0;
270
271                 /* We cant block a part or quit, so instead we change the reason to 'Reason filtered' */
272                 command_t* c = ServerInstance->Parser->GetHandler(command);
273                 if (c)
274                 {
275                         const char* params[127];
276                         for (int item = 0; item < pcnt; item++)
277                                 params[item] = parameters[item];
278                         params[replacepoint] = "Reason filtered";
279
280                         /* We're blocking, OR theyre quitting and its a KILL action
281                          * (we cant kill someone whos already quitting, so filter them anyway)
282                          */
283                         if ((f->action == "block") || (((!parting) && (f->action == "kill"))) || (f->action == "silent"))
284                         {
285                                 c->Handle(params, pcnt, user);
286                                 return 1;
287                         }
288                         else
289                         {
290                                 /* Are they parting, if so, kill is applicable */
291                                 if ((parting) && (f->action == "kill"))
292                                 {
293                                         user->SetWriteError("Filtered: "+f->reason);
294                                         /* This WriteServ causes the write error to be applied.
295                                          * Its not safe to kill here with QuitUser in a PreCommand handler,
296                                          * so we do it this way, which is safe just about anywhere.
297                                          */
298                                         user->WriteServ("NOTICE %s :*** Your PART message was filtered: %s", user->nick, f->reason.c_str());
299                                 }
300                                 if (f->action == "gline")
301                                 {
302                                         /* Note: We gline *@IP so that if their host doesnt resolve the gline still applies. */
303                                         std::string wild = "*@";
304                                         wild.append(user->GetIPString());
305
306                                         if (ServerInstance->XLines->add_gline(f->gline_time, ServerInstance->Config->ServerName, f->reason.c_str(), wild.c_str()))
307                                         {
308                                                 ServerInstance->XLines->apply_lines(APPLY_GLINES);
309                                                 FOREACH_MOD(I_OnAddGLine,OnAddGLine(f->gline_time, NULL, f->reason, user->MakeHostIP()));
310                                         }
311                                 }
312                                 return 1;
313                         }
314                 }
315                 return 0;
316         }
317         return 0;
318 }
319
320 void FilterBase::OnRehash(userrec* user, const std::string &parameter)
321 {
322 }
323         
324 Version FilterBase::GetVersion()
325 {
326         return Version(1,1,0,2,VF_VENDOR|VF_COMMON,API_VERSION);
327 }
328
329
330 std::string FilterBase::EncodeFilter(FilterResult* filter)
331 {
332         std::ostringstream stream;
333         std::string x = filter->freeform;
334
335         for (std::string::iterator n = x.begin(); n != x.end(); n++)
336                 if (*n == ' ')
337                         *n = '\7';
338
339         stream << x << " " << filter->action << " " << filter->opers_exempt << " " << filter->gline_time << " " << filter->reason;
340         return stream.str();
341 }
342
343 FilterResult FilterBase::DecodeFilter(const std::string &data)
344 {
345         FilterResult res;
346         std::istringstream stream(data);
347
348         stream >> res.freeform;
349         stream >> res.action;
350         stream >> res.opers_exempt;
351         stream >> res.gline_time;
352         res.reason = stream.str();
353
354         for (std::string::iterator n = res.freeform.begin(); n != res.freeform.end(); n++)
355                 if (*n == '\7')
356                         *n = ' ';
357
358         return res;
359 }
360
361 void FilterBase::OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable)
362 {
363         this->SyncFilters(proto, opaque);
364 }
365
366 void FilterBase::SendFilter(Module* proto, void* opaque, FilterResult* iter)
367 {
368         proto->ProtoSendMetaData(opaque, TYPE_OTHER, NULL, "filter", EncodeFilter(iter));
369 }
370
371 void FilterBase::OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
372 {
373         if ((target_type == TYPE_OTHER) && (extname == "filter"))
374         {
375                 FilterResult data = DecodeFilter(extdata);
376                 this->AddFilter(data.freeform, data.action, data.reason, data.gline_time, data.opers_exempt);
377         }
378 }
379