]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_joinflood.cpp
fde974559bbb68f1820f5669c8d8e7fc054ae675
[user/henk/code/inspircd.git] / src / modules / m_joinflood.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
6  *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
7  *   Copyright (C) 2006-2007 Craig Edwards <craigedwards@brainbox.cc>
8  *   Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
9  *
10  * This file is part of InspIRCd.  InspIRCd is free software: you can
11  * redistribute it and/or modify it under the terms of the GNU General Public
12  * License as published by the Free Software Foundation, version 2.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23
24 #include "inspircd.h"
25
26 /* $ModDesc: Provides channel mode +j (join flood protection) */
27
28 /** Holds settings and state associated with channel mode +j
29  */
30 class joinfloodsettings
31 {
32  public:
33         unsigned int secs;
34         unsigned int joins;
35         time_t reset;
36         time_t unlocktime;
37         unsigned int counter;
38
39         joinfloodsettings(unsigned int b, unsigned int c)
40                 : secs(b), joins(c), unlocktime(0), counter(0)
41         {
42                 reset = ServerInstance->Time() + secs;
43         }
44
45         void addjoin()
46         {
47                 if (ServerInstance->Time() > reset)
48                 {
49                         counter = 1;
50                         reset = ServerInstance->Time() + secs;
51                 }
52                 else
53                         counter++;
54         }
55
56         bool shouldlock()
57         {
58                 return (counter >= this->joins);
59         }
60
61         void clear()
62         {
63                 counter = 0;
64         }
65
66         bool islocked()
67         {
68                 if (ServerInstance->Time() > unlocktime)
69                         unlocktime = 0;
70
71                 return (unlocktime != 0);
72         }
73
74         void lock()
75         {
76                 unlocktime = ServerInstance->Time() + 60;
77         }
78
79         bool operator==(const joinfloodsettings& other) const
80         {
81                 return ((this->secs == other.secs) && (this->joins == other.joins));
82         }
83 };
84
85 /** Handles channel mode +j
86  */
87 class JoinFlood : public ModeHandler
88 {
89  public:
90         SimpleExtItem<joinfloodsettings> ext;
91         JoinFlood(Module* Creator) : ModeHandler(Creator, "joinflood", 'j', PARAM_SETONLY, MODETYPE_CHANNEL),
92                 ext("joinflood", Creator) { }
93
94         ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
95         {
96                 if (adding)
97                 {
98                         std::string::size_type colon = parameter.find(':');
99                         if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
100                         {
101                                 source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
102                                 return MODEACTION_DENY;
103                         }
104
105                         /* Set up the flood parameters for this channel */
106                         unsigned int njoins = ConvToInt(parameter.substr(0, colon));
107                         unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
108                         if ((njoins<1) || (nsecs<1))
109                         {
110                                 source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
111                                 return MODEACTION_DENY;
112                         }
113
114                         joinfloodsettings jfs(nsecs, njoins);
115                         joinfloodsettings* f = ext.get(channel);
116                         if ((f) && (*f == jfs))
117                                 // mode params match
118                                 return MODEACTION_DENY;
119
120                         ext.set(channel, jfs);
121                         parameter = ConvToStr(njoins) + ":" + ConvToStr(nsecs);
122                         channel->SetModeParam(this, parameter);
123                         return MODEACTION_ALLOW;
124                 }
125                 else
126                 {
127                         if (!channel->IsModeSet(this))
128                                 return MODEACTION_DENY;
129
130                         joinfloodsettings* f = ext.get(channel);
131                         if (f)
132                         {
133                                 ext.unset(channel);
134                                 channel->SetModeParam(this, "");
135                                 return MODEACTION_ALLOW;
136                         }
137                 }
138                 return MODEACTION_DENY;
139         }
140 };
141
142 class ModuleJoinFlood : public Module
143 {
144         JoinFlood jf;
145
146  public:
147         ModuleJoinFlood()
148                 : jf(this)
149         {
150         }
151
152         void init() CXX11_OVERRIDE
153         {
154                 ServerInstance->Modules->AddService(jf);
155                 ServerInstance->Modules->AddService(jf.ext);
156                 Implementation eventlist[] = { I_OnUserPreJoin, I_OnUserJoin };
157                 ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
158         }
159
160         ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
161         {
162                 if (chan)
163                 {
164                         joinfloodsettings *f = jf.ext.get(chan);
165                         if (f && f->islocked())
166                         {
167                                 user->WriteNumeric(609, "%s %s :This channel is temporarily unavailable (+j). Please try again later.",user->nick.c_str(),chan->name.c_str());
168                                 return MOD_RES_DENY;
169                         }
170                 }
171                 return MOD_RES_PASSTHRU;
172         }
173
174         void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
175         {
176                 /* We arent interested in JOIN events caused by a network burst */
177                 if (sync)
178                         return;
179
180                 joinfloodsettings *f = jf.ext.get(memb->chan);
181
182                 /* But all others are OK */
183                 if (f)
184                 {
185                         f->addjoin();
186                         if (f->shouldlock())
187                         {
188                                 f->clear();
189                                 f->lock();
190                                 memb->chan->WriteChannelWithServ((char*)ServerInstance->Config->ServerName.c_str(), "NOTICE %s :This channel has been closed to new users for 60 seconds because there have been more than %d joins in %d seconds.", memb->chan->name.c_str(), f->joins, f->secs);
191                         }
192                 }
193         }
194
195         Version GetVersion() CXX11_OVERRIDE
196         {
197                 return Version("Provides channel mode +j (join flood protection)", VF_VENDOR);
198         }
199 };
200
201 MODULE_INIT(ModuleJoinFlood)