]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_joinflood.cpp
53277150a43e1f0b2ae977db4b14234fcd73db3c
[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                         return MODEACTION_ALLOW;
123                 }
124                 else
125                 {
126                         if (!channel->IsModeSet(this))
127                                 return MODEACTION_DENY;
128
129                         joinfloodsettings* f = ext.get(channel);
130                         if (f)
131                         {
132                                 ext.unset(channel);
133                                 return MODEACTION_ALLOW;
134                         }
135                 }
136                 return MODEACTION_DENY;
137         }
138 };
139
140 class ModuleJoinFlood : public Module
141 {
142         JoinFlood jf;
143
144  public:
145         ModuleJoinFlood()
146                 : jf(this)
147         {
148         }
149
150         void init() CXX11_OVERRIDE
151         {
152                 ServerInstance->Modules->AddService(jf);
153                 ServerInstance->Modules->AddService(jf.ext);
154                 Implementation eventlist[] = { I_OnUserPreJoin, I_OnUserJoin };
155                 ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
156         }
157
158         ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE
159         {
160                 if (chan)
161                 {
162                         joinfloodsettings *f = jf.ext.get(chan);
163                         if (f && f->islocked())
164                         {
165                                 user->WriteNumeric(609, "%s %s :This channel is temporarily unavailable (+j). Please try again later.",user->nick.c_str(),chan->name.c_str());
166                                 return MOD_RES_DENY;
167                         }
168                 }
169                 return MOD_RES_PASSTHRU;
170         }
171
172         void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
173         {
174                 /* We arent interested in JOIN events caused by a network burst */
175                 if (sync)
176                         return;
177
178                 joinfloodsettings *f = jf.ext.get(memb->chan);
179
180                 /* But all others are OK */
181                 if (f)
182                 {
183                         f->addjoin();
184                         if (f->shouldlock())
185                         {
186                                 f->clear();
187                                 f->lock();
188                                 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);
189                         }
190                 }
191         }
192
193         Version GetVersion() CXX11_OVERRIDE
194         {
195                 return Version("Provides channel mode +j (join flood protection)", VF_VENDOR);
196         }
197 };
198
199 MODULE_INIT(ModuleJoinFlood)