]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_messageflood.cpp
Fix some of the include guard names (requested by SaberUK)
[user/henk/code/inspircd.git] / src / modules / m_messageflood.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2010 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 channel mode +f (message flood protection) */
17
18 /** Holds flood settings and state for mode +f
19  */
20 class floodsettings
21 {
22  public:
23         bool ban;
24         int secs;
25         int lines;
26         time_t reset;
27         std::map<User*,int> counters;
28
29         floodsettings(bool a, int b, int c) : ban(a), secs(b), lines(c)
30         {
31                 reset = ServerInstance->Time() + secs;
32         };
33
34         void addmessage(User* who)
35         {
36                 std::map<User*,int>::iterator iter = counters.find(who);
37                 if (iter != counters.end())
38                 {
39                         iter->second++;
40                 }
41                 else
42                 {
43                         counters[who] = 1;
44                 }
45                 if (ServerInstance->Time() > reset)
46                 {
47                         counters.clear();
48                         reset = ServerInstance->Time() + secs;
49                 }
50         }
51
52         bool shouldkick(User* who)
53         {
54                 std::map<User*,int>::iterator iter = counters.find(who);
55                 if (iter != counters.end())
56                 {
57                         return (iter->second >= this->lines);
58                 }
59                 else return false;
60         }
61
62         void clear(User* who)
63         {
64                 std::map<User*,int>::iterator iter = counters.find(who);
65                 if (iter != counters.end())
66                 {
67                         counters.erase(iter);
68                 }
69         }
70 };
71
72 /** Handles channel mode +f
73  */
74 class MsgFlood : public ModeHandler
75 {
76  public:
77         SimpleExtItem<floodsettings> ext;
78         MsgFlood(Module* Creator) : ModeHandler(Creator, "flood", 'f', PARAM_SETONLY, MODETYPE_CHANNEL),
79                 ext("messageflood", Creator) { }
80
81         ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
82         {
83                 floodsettings *f = ext.get(channel);
84
85                 if (adding)
86                 {
87                         char ndata[MAXBUF];
88                         char* data = ndata;
89                         strlcpy(ndata,parameter.c_str(),MAXBUF);
90                         char* lines = data;
91                         char* secs = NULL;
92                         bool ban = false;
93                         if (*data == '*')
94                         {
95                                 ban = true;
96                                 lines++;
97                         }
98                         else
99                         {
100                                 ban = false;
101                         }
102                         while (*data)
103                         {
104                                 if (*data == ':')
105                                 {
106                                         *data = 0;
107                                         data++;
108                                         secs = data;
109                                         break;
110                                 }
111                                 else data++;
112                         }
113                         if (secs)
114                         {
115                                 /* Set up the flood parameters for this channel */
116                                 int nlines = atoi(lines);
117                                 int nsecs = atoi(secs);
118                                 if ((nlines<2) || (nsecs<1))
119                                 {
120                                         source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
121                                         parameter.clear();
122                                         return MODEACTION_DENY;
123                                 }
124                                 else
125                                 {
126                                         if (!f)
127                                         {
128                                                 parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" +ConvToStr(nsecs);
129                                                 f = new floodsettings(ban,nsecs,nlines);
130                                                 ext.set(channel, f);
131                                                 channel->SetModeParam('f', parameter);
132                                                 return MODEACTION_ALLOW;
133                                         }
134                                         else
135                                         {
136                                                 std::string cur_param = channel->GetModeParameter('f');
137                                                 parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" +ConvToStr(nsecs);
138                                                 if (cur_param == parameter)
139                                                 {
140                                                         // mode params match
141                                                         return MODEACTION_DENY;
142                                                 }
143                                                 else
144                                                 {
145                                                         if ((((nlines != f->lines) || (nsecs != f->secs) || (ban != f->ban))) && (((nsecs > 0) && (nlines > 0))))
146                                                         {
147                                                                 floodsettings *fs = new floodsettings(ban,nsecs,nlines);
148                                                                 ext.set(channel, fs);
149                                                                 channel->SetModeParam('f', parameter);
150                                                                 return MODEACTION_ALLOW;
151                                                         }
152                                                         else
153                                                         {
154                                                                 return MODEACTION_DENY;
155                                                         }
156                                                 }
157                                         }
158                                 }
159                         }
160                         else
161                         {
162                                 source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
163                                 parameter.clear();
164                                 return MODEACTION_DENY;
165                         }
166                 }
167                 else
168                 {
169                         if (f)
170                         {
171                                 ext.unset(channel);
172                                 channel->SetModeParam('f', "");
173                                 return MODEACTION_ALLOW;
174                         }
175                 }
176
177                 return MODEACTION_DENY;
178         }
179 };
180
181 class ModuleMsgFlood : public Module
182 {
183         MsgFlood mf;
184
185  public:
186
187         ModuleMsgFlood()
188                 : mf(this)
189         {
190                 if (!ServerInstance->Modes->AddMode(&mf))
191                         throw ModuleException("Could not add new modes!");
192                 ServerInstance->Extensions.Register(&mf.ext);
193                 Implementation eventlist[] = { I_OnUserPreNotice, I_OnUserPreMessage };
194                 ServerInstance->Modules->Attach(eventlist, this, 2);
195         }
196
197         ModResult ProcessMessages(User* user,Channel* dest, const std::string &text)
198         {
199                 ModResult res = ServerInstance->OnCheckExemption(user,dest,"flood");
200                 if (!IS_LOCAL(user) || res == MOD_RES_ALLOW)
201                         return MOD_RES_PASSTHRU;
202
203                 floodsettings *f = mf.ext.get(dest);
204                 if (f)
205                 {
206                         f->addmessage(user);
207                         if (f->shouldkick(user))
208                         {
209                                 /* Youre outttta here! */
210                                 f->clear(user);
211                                 if (f->ban)
212                                 {
213                                         std::vector<std::string> parameters;
214                                         parameters.push_back(dest->name);
215                                         parameters.push_back("+b");
216                                         parameters.push_back(user->MakeWildHost());
217                                         ServerInstance->SendGlobalMode(parameters, ServerInstance->FakeClient);
218                                 }
219
220                                 char kickmessage[MAXBUF];
221                                 snprintf(kickmessage, MAXBUF, "Channel flood triggered (limit is %d lines in %d secs)", f->lines, f->secs);
222
223                                 dest->KickUser(ServerInstance->FakeClient, user, kickmessage);
224
225                                 return MOD_RES_DENY;
226                         }
227                 }
228
229                 return MOD_RES_PASSTHRU;
230         }
231
232         ModResult OnUserPreMessage(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
233         {
234                 if (target_type == TYPE_CHANNEL)
235                         return ProcessMessages(user,(Channel*)dest,text);
236
237                 return MOD_RES_PASSTHRU;
238         }
239
240         ModResult OnUserPreNotice(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
241         {
242                 if (target_type == TYPE_CHANNEL)
243                         return ProcessMessages(user,(Channel*)dest,text);
244
245                 return MOD_RES_PASSTHRU;
246         }
247
248         ~ModuleMsgFlood()
249         {
250         }
251
252         Version GetVersion()
253         {
254                 return Version("Provides channel mode +f (message flood protection)", VF_VENDOR);
255         }
256 };
257
258 MODULE_INIT(ModuleMsgFlood)