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