]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_nickflood.cpp
ce8b364e45a36fa28fae1740947c3e02abff31ca
[user/henk/code/inspircd.git] / src / modules / m_nickflood.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2007, 2009 Robin Burchell <robin+git@viroteck.net>
6  *
7  * This file is part of InspIRCd.  InspIRCd is free software: you can
8  * redistribute it and/or modify it under the terms of the GNU General Public
9  * License as published by the Free Software Foundation, version 2.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20
21 #include "inspircd.h"
22 #include "modules/exemption.h"
23
24 // The number of seconds nickname changing will be blocked for.
25 static unsigned int duration;
26
27 /** Holds settings and state associated with channel mode +F
28  */
29 class nickfloodsettings
30 {
31  public:
32         unsigned int secs;
33         unsigned int nicks;
34         time_t reset;
35         time_t unlocktime;
36         unsigned int counter;
37
38         nickfloodsettings(unsigned int b, unsigned int c)
39                 : secs(b), nicks(c), unlocktime(0), counter(0)
40         {
41                 reset = ServerInstance->Time() + secs;
42         }
43
44         void addnick()
45         {
46                 if (ServerInstance->Time() > reset)
47                 {
48                         counter = 1;
49                         reset = ServerInstance->Time() + secs;
50                 }
51                 else
52                         counter++;
53         }
54
55         bool shouldlock()
56         {
57                 /* XXX HACK: using counter + 1 here now to allow the counter to only be incremented
58                  * on successful nick changes; this will be checked before the counter is
59                  * incremented.
60                  */
61                 return ((ServerInstance->Time() <= reset) && (counter == this->nicks));
62         }
63
64         void clear()
65         {
66                 counter = 0;
67         }
68
69         bool islocked()
70         {
71                 if (ServerInstance->Time() > unlocktime)
72                         unlocktime = 0;
73
74                 return (unlocktime != 0);
75         }
76
77         void lock()
78         {
79                 unlocktime = ServerInstance->Time() + duration;
80         }
81 };
82
83 /** Handles channel mode +F
84  */
85 class NickFlood : public ParamMode<NickFlood, SimpleExtItem<nickfloodsettings> >
86 {
87  public:
88         NickFlood(Module* Creator)
89                 : ParamMode<NickFlood, SimpleExtItem<nickfloodsettings> >(Creator, "nickflood", 'F')
90         {
91         }
92
93         ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
94         {
95                 std::string::size_type colon = parameter.find(':');
96                 if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
97                 {
98                         source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
99                         return MODEACTION_DENY;
100                 }
101
102                 /* Set up the flood parameters for this channel */
103                 unsigned int nnicks = ConvToNum<unsigned int>(parameter.substr(0, colon));
104                 unsigned int nsecs = ConvToNum<unsigned int>(parameter.substr(colon+1));
105
106                 if ((nnicks<1) || (nsecs<1))
107                 {
108                         source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
109                         return MODEACTION_DENY;
110                 }
111
112                 ext.set(channel, new nickfloodsettings(nsecs, nnicks));
113                 return MODEACTION_ALLOW;
114         }
115
116         void SerializeParam(Channel* chan, const nickfloodsettings* nfs, std::string& out)
117         {
118                 out.append(ConvToStr(nfs->nicks)).push_back(':');
119                 out.append(ConvToStr(nfs->secs));
120         }
121 };
122
123 class ModuleNickFlood : public Module
124 {
125         CheckExemption::EventProvider exemptionprov;
126         NickFlood nf;
127
128  public:
129         ModuleNickFlood()
130                 : exemptionprov(this)
131                 , nf(this)
132         {
133         }
134
135         void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
136         {
137                 ConfigTag* tag = ServerInstance->Config->ConfValue("nickflood");
138                 duration = tag->getDuration("duration", 60, 10, 600);
139         }
140
141         ModResult OnUserPreNick(LocalUser* user, const std::string& newnick) CXX11_OVERRIDE
142         {
143                 for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); i++)
144                 {
145                         Channel* channel = (*i)->chan;
146                         ModResult res;
147
148                         nickfloodsettings *f = nf.ext.get(channel);
149                         if (f)
150                         {
151                                 res = CheckExemption::Call(exemptionprov, user, channel, "nickflood");
152                                 if (res == MOD_RES_ALLOW)
153                                         continue;
154
155                                 if (f->islocked())
156                                 {
157                                         user->WriteNumeric(ERR_CANTCHANGENICK, InspIRCd::Format("%s has been locked for nickchanges for %u seconds because there have been more than %u nick changes in %u seconds", channel->name.c_str(), duration, f->nicks, f->secs));
158                                         return MOD_RES_DENY;
159                                 }
160
161                                 if (f->shouldlock())
162                                 {
163                                         f->clear();
164                                         f->lock();
165                                         channel->WriteNotice(InspIRCd::Format("No nick changes are allowed for %u seconds because there have been more than %u nick changes in %u seconds.", duration, f->nicks, f->secs));
166                                         return MOD_RES_DENY;
167                                 }
168                         }
169                 }
170
171                 return MOD_RES_PASSTHRU;
172         }
173
174         /*
175          * XXX: HACK: We do the increment on the *POST* event here (instead of all together) because we have no way of knowing whether other modules would block a nickchange.
176          */
177         void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
178         {
179                 if (isdigit(user->nick[0])) /* allow switches to UID */
180                         return;
181
182                 for (User::ChanList::iterator i = user->chans.begin(); i != user->chans.end(); ++i)
183                 {
184                         Channel* channel = (*i)->chan;
185                         ModResult res;
186
187                         nickfloodsettings *f = nf.ext.get(channel);
188                         if (f)
189                         {
190                                 res = CheckExemption::Call(exemptionprov, user, channel, "nickflood");
191                                 if (res == MOD_RES_ALLOW)
192                                         return;
193
194                                 /* moved this here to avoid incrementing the counter for nick
195                                  * changes that are denied for some other reason (bans, +N, etc.)
196                                  * per bug #874.
197                                  */
198                                 f->addnick();
199                         }
200                 }
201         }
202
203         Version GetVersion() CXX11_OVERRIDE
204         {
205                 return Version("Channel mode F - nick flood protection", VF_VENDOR);
206         }
207 };
208
209 MODULE_INIT(ModuleNickFlood)