]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_joinflood.cpp
28b12a0860ad29f5e324ac3782195ca483655c4c
[user/henk/code/inspircd.git] / src / modules / m_joinflood.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2013, 2016-2019 Sadie Powell <sadie@witchery.services>
5  *   Copyright (C) 2012-2014 Attila Molnar <attilamolnar@hush.com>
6  *   Copyright (C) 2012, 2019 Robby <robby@chatbelgie.be>
7  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
8  *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
9  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
10  *   Copyright (C) 2006-2007, 2010 Craig Edwards <brain@inspircd.org>
11  *
12  * This file is part of InspIRCd.  InspIRCd is free software: you can
13  * redistribute it and/or modify it under the terms of the GNU General Public
14  * License as published by the Free Software Foundation, version 2.
15  *
16  * This program is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
19  * details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24
25
26 #include "inspircd.h"
27 #include "modules/server.h"
28
29 enum
30 {
31         // From RFC 2182.
32         ERR_UNAVAILRESOURCE = 437
33 };
34
35 // The number of seconds the channel will be closed for.
36 static unsigned int duration;
37
38 /** Holds settings and state associated with channel mode +j
39  */
40 class joinfloodsettings
41 {
42  public:
43         unsigned int secs;
44         unsigned int joins;
45         time_t reset;
46         time_t unlocktime;
47         unsigned int counter;
48
49         joinfloodsettings(unsigned int b, unsigned int c)
50                 : secs(b), joins(c), unlocktime(0), counter(0)
51         {
52                 reset = ServerInstance->Time() + secs;
53         }
54
55         void addjoin()
56         {
57                 if (ServerInstance->Time() > reset)
58                 {
59                         counter = 1;
60                         reset = ServerInstance->Time() + secs;
61                 }
62                 else
63                         counter++;
64         }
65
66         bool shouldlock()
67         {
68                 return (counter >= this->joins);
69         }
70
71         void clear()
72         {
73                 counter = 0;
74         }
75
76         bool islocked()
77         {
78                 if (ServerInstance->Time() > unlocktime)
79                         unlocktime = 0;
80
81                 return (unlocktime != 0);
82         }
83
84         void lock()
85         {
86                 unlocktime = ServerInstance->Time() + duration;
87         }
88
89         bool operator==(const joinfloodsettings& other) const
90         {
91                 return ((this->secs == other.secs) && (this->joins == other.joins));
92         }
93 };
94
95 /** Handles channel mode +j
96  */
97 class JoinFlood : public ParamMode<JoinFlood, SimpleExtItem<joinfloodsettings> >
98 {
99  public:
100         JoinFlood(Module* Creator)
101                 : ParamMode<JoinFlood, SimpleExtItem<joinfloodsettings> >(Creator, "joinflood", 'j')
102         {
103                 syntax = "<joins>:<seconds>";
104         }
105
106         ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
107         {
108                 std::string::size_type colon = parameter.find(':');
109                 if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
110                 {
111                         source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
112                         return MODEACTION_DENY;
113                 }
114
115                 /* Set up the flood parameters for this channel */
116                 unsigned int njoins = ConvToNum<unsigned int>(parameter.substr(0, colon));
117                 unsigned int nsecs = ConvToNum<unsigned int>(parameter.substr(colon+1));
118                 if ((njoins<1) || (nsecs<1))
119                 {
120                         source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
121                         return MODEACTION_DENY;
122                 }
123
124                 ext.set(channel, new joinfloodsettings(nsecs, njoins));
125                 return MODEACTION_ALLOW;
126         }
127
128         void SerializeParam(Channel* chan, const joinfloodsettings* jfs, std::string& out)
129         {
130                 out.append(ConvToStr(jfs->joins)).push_back(':');
131                 out.append(ConvToStr(jfs->secs));
132         }
133 };
134
135 class ModuleJoinFlood
136         : public Module
137         , public ServerProtocol::LinkEventListener
138 {
139  private:
140         JoinFlood jf;
141         time_t ignoreuntil;
142         unsigned long bootwait;
143         unsigned long splitwait;
144
145  public:
146         // Stop GCC warnings about the deprecated OnServerSplit event.
147         using ServerProtocol::LinkEventListener::OnServerSplit;
148
149         ModuleJoinFlood()
150                 : ServerProtocol::LinkEventListener(this)
151                 , jf(this)
152                 , ignoreuntil(0)
153         {
154         }
155
156         void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
157         {
158                 ConfigTag* tag = ServerInstance->Config->ConfValue("joinflood");
159                 duration = tag->getDuration("duration", 60, 10, 600);
160                 bootwait = tag->getDuration("bootwait", 30);
161                 splitwait = tag->getDuration("splitwait", 30);
162
163                 ignoreuntil = ServerInstance->startup_time + bootwait;
164         }
165
166         void OnServerSplit(const Server* server, bool error) CXX11_OVERRIDE
167         {
168                 if (splitwait)
169                         ignoreuntil = ServerInstance->Time() + splitwait;
170         }
171
172         ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
173         {
174                 if (chan)
175                 {
176                         joinfloodsettings *f = jf.ext.get(chan);
177                         if (f && f->islocked())
178                         {
179                                 user->WriteNumeric(ERR_UNAVAILRESOURCE, chan->name, "This channel is temporarily unavailable (+j is set). Please try again later.");
180                                 return MOD_RES_DENY;
181                         }
182                 }
183                 return MOD_RES_PASSTHRU;
184         }
185
186         void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
187         {
188                 /* We arent interested in JOIN events caused by a network burst */
189                 if (sync || ignoreuntil > ServerInstance->Time())
190                         return;
191
192                 joinfloodsettings *f = jf.ext.get(memb->chan);
193
194                 /* But all others are OK */
195                 if ((f) && (!f->islocked()))
196                 {
197                         f->addjoin();
198                         if (f->shouldlock())
199                         {
200                                 f->clear();
201                                 f->lock();
202                                 memb->chan->WriteNotice(InspIRCd::Format("This channel has been closed to new users for %u seconds because there have been more than %d joins in %d seconds.", duration, f->joins, f->secs));
203                         }
204                 }
205         }
206
207         Version GetVersion() CXX11_OVERRIDE
208         {
209                 return Version("Adds channel mode j (joinflood) which helps protect against spammers which mass-join channels.", VF_VENDOR);
210         }
211 };
212
213 MODULE_INIT(ModuleJoinFlood)