]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/coremods/core_channel/core_channel.cpp
ccf4d1a6e1c64695b1ecf1a632054066036bbed5
[user/henk/code/inspircd.git] / src / coremods / core_channel / core_channel.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2014-2015 Attila Molnar <attilamolnar@hush.com>
5  *
6  * This file is part of InspIRCd.  InspIRCd is free software: you can
7  * redistribute it and/or modify it under the terms of the GNU General Public
8  * License as published by the Free Software Foundation, version 2.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19
20 #include "inspircd.h"
21 #include "core_channel.h"
22 #include "invite.h"
23 #include "listmode.h"
24
25 class CoreModChannel : public Module, public CheckExemption::EventListener
26 {
27         Invite::APIImpl invapi;
28         CommandInvite cmdinvite;
29         CommandJoin cmdjoin;
30         CommandKick cmdkick;
31         CommandNames cmdnames;
32         CommandTopic cmdtopic;
33
34         ModeChannelBan banmode;
35         SimpleChannelModeHandler inviteonlymode;
36         ModeChannelKey keymode;
37         ModeChannelLimit limitmode;
38         SimpleChannelModeHandler moderatedmode;
39         SimpleChannelModeHandler noextmsgmode;
40         ModeChannelOp opmode;
41         SimpleChannelModeHandler privatemode;
42         SimpleChannelModeHandler secretmode;
43         SimpleChannelModeHandler topiclockmode;
44         ModeChannelVoice voicemode;
45
46         insp::flat_map<std::string, char> exemptions;
47
48         ModResult IsInvited(User* user, Channel* chan)
49         {
50                 LocalUser* localuser = IS_LOCAL(user);
51                 if ((localuser) && (invapi.IsInvited(localuser, chan)))
52                         return MOD_RES_ALLOW;
53                 return MOD_RES_PASSTHRU;
54         }
55
56  public:
57         CoreModChannel()
58                 : CheckExemption::EventListener(this)
59                 , invapi(this)
60                 , cmdinvite(this, invapi)
61                 , cmdjoin(this)
62                 , cmdkick(this)
63                 , cmdnames(this)
64                 , cmdtopic(this)
65                 , banmode(this)
66                 , inviteonlymode(this, "inviteonly", 'i')
67                 , keymode(this)
68                 , limitmode(this)
69                 , moderatedmode(this, "moderated", 'm')
70                 , noextmsgmode(this, "noextmsg", 'n')
71                 , opmode(this)
72                 , privatemode(this, "private", 'p')
73                 , secretmode(this, "secret", 's')
74                 , topiclockmode(this, "topiclock", 't')
75                 , voicemode(this)
76         {
77         }
78
79         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
80         {
81                 ConfigTag* optionstag = ServerInstance->Config->ConfValue("options");
82                 Implementation events[] = { I_OnCheckKey, I_OnCheckLimit, I_OnCheckChannelBan };
83                 if (optionstag->getBool("invitebypassmodes", true))
84                         ServerInstance->Modules.Attach(events, this, sizeof(events)/sizeof(Implementation));
85                 else
86                 {
87                         for (unsigned int i = 0; i < sizeof(events)/sizeof(Implementation); i++)
88                                 ServerInstance->Modules.Detach(events[i], this);
89                 }
90
91                 std::string current;
92                 irc::spacesepstream defaultstream(optionstag->getString("exemptchanops"));
93                 insp::flat_map<std::string, char> exempts;
94                 while (defaultstream.GetToken(current))
95                 {
96                         std::string::size_type pos = current.find(':');
97                         if (pos == std::string::npos || (pos + 2) > current.size())
98                                 throw ModuleException("Invalid exemptchanops value '" + current + "' at " + optionstag->getTagLocation());
99
100                         const std::string restriction = current.substr(0, pos);
101                         const char prefix = current[pos + 1];
102
103                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Exempting prefix %c from %s", prefix, restriction.c_str());
104                         exempts[restriction] = prefix;
105                 }
106                 exemptions.swap(exempts);
107
108                 ConfigTag* securitytag = ServerInstance->Config->ConfValue("security");
109                 const std::string announceinvites = securitytag->getString("announceinvites", "dynamic");
110                 if (stdalgo::string::equalsci(announceinvites, "none"))
111                         cmdinvite.announceinvites = Invite::ANNOUNCE_NONE;
112                 else if (stdalgo::string::equalsci(announceinvites, "all"))
113                         cmdinvite.announceinvites = Invite::ANNOUNCE_ALL;
114                 else if (stdalgo::string::equalsci(announceinvites, "ops"))
115                         cmdinvite.announceinvites = Invite::ANNOUNCE_OPS;
116                 else if (stdalgo::string::equalsci(announceinvites, "dynamic"))
117                         cmdinvite.announceinvites = Invite::ANNOUNCE_DYNAMIC;
118                 else
119                         throw ModuleException(announceinvites + " is an invalid <security:announceinvites> value, at " + securitytag->getTagLocation());
120
121                 // In 2.0 we allowed limits of 0 to be set. This is non-standard behaviour
122                 // and will be removed in the next major release.
123                 limitmode.minlimit = optionstag->getBool("allowzerolimit", true) ? 0 : 1;
124
125                 banmode.DoRehash();
126         }
127
128         void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
129         {
130                 tokens["KEYLEN"] = ConvToStr(ModeChannelKey::maxkeylen);
131
132                 // Build a map of limits to their mode character.
133                 insp::flat_map<int, std::string> limits;
134                 const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
135                 for (ModeParser::ListModeList::const_iterator iter = listmodes.begin(); iter != listmodes.end(); ++iter)
136                 {
137                         const unsigned int limit = (*iter)->GetLowerLimit();
138                         limits[limit].push_back((*iter)->GetModeChar());
139                 }
140
141                 // Generate the MAXLIST token from the limits map.
142                 std::string& buffer = tokens["MAXLIST"];
143                 for (insp::flat_map<int, std::string>::const_iterator iter = limits.begin(); iter != limits.end(); ++iter)
144                 {
145                         if (!buffer.empty())
146                                 buffer.push_back(',');
147
148                         buffer.append(iter->second);
149                         buffer.push_back(':');
150                         buffer.append(ConvToStr(iter->first));
151                 }
152         }
153
154         ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string&, std::string&, const std::string& keygiven) CXX11_OVERRIDE
155         {
156                 if (!chan)
157                         return MOD_RES_PASSTHRU;
158
159                 // Check whether the channel key is correct.
160                 const std::string ckey = chan->GetModeParameter(&keymode);
161                 if (!ckey.empty())
162                 {
163                         ModResult MOD_RESULT;
164                         FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, chan, keygiven));
165                         if (!MOD_RESULT.check(InspIRCd::TimingSafeCompare(ckey, keygiven)))
166                         {
167                                 // If no key provided, or key is not the right one, and can't bypass +k (not invited or option not enabled)
168                                 user->WriteNumeric(ERR_BADCHANNELKEY, chan->name, "Cannot join channel (Incorrect channel key)");
169                                 return MOD_RES_DENY;
170                         }
171                 }
172
173                 // Check whether the invite only mode is set.
174                 if (chan->IsModeSet(inviteonlymode))
175                 {
176                         ModResult MOD_RESULT;
177                         FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, chan));
178                         if (MOD_RESULT != MOD_RES_ALLOW)
179                         {
180                                 user->WriteNumeric(ERR_INVITEONLYCHAN, chan->name, "Cannot join channel (Invite only)");
181                                 return MOD_RES_DENY;
182                         }
183                 }
184
185                 // Check whether the limit would be exceeded by this user joining.
186                 if (chan->IsModeSet(limitmode))
187                 {
188                         ModResult MOD_RESULT;
189                         FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, chan));
190                         if (!MOD_RESULT.check(chan->GetUserCounter() < static_cast<size_t>(limitmode.ext.get(chan))))
191                         {
192                                 user->WriteNumeric(ERR_CHANNELISFULL, chan->name, "Cannot join channel (Channel is full)");
193                                 return MOD_RES_DENY;
194                         }
195                 }
196
197                 // Everything looks okay.
198                 return MOD_RES_PASSTHRU;
199         }
200
201         void OnPostJoin(Membership* memb) CXX11_OVERRIDE
202         {
203                 Channel* const chan = memb->chan;
204                 LocalUser* const localuser = IS_LOCAL(memb->user);
205                 if (localuser)
206                 {
207                         // Remove existing invite, if any
208                         invapi.Remove(localuser, chan);
209
210                         if (chan->topic.length())
211                                 Topic::ShowTopic(localuser, chan);
212
213                         // Show all members of the channel, including invisible (+i) users
214                         cmdnames.SendNames(localuser, chan, true);
215                 }
216         }
217
218         ModResult OnCheckKey(User* user, Channel* chan, const std::string& keygiven) CXX11_OVERRIDE
219         {
220                 // Hook only runs when being invited bypasses +bkl
221                 return IsInvited(user, chan);
222         }
223
224         ModResult OnCheckChannelBan(User* user, Channel* chan) CXX11_OVERRIDE
225         {
226                 // Hook only runs when being invited bypasses +bkl
227                 return IsInvited(user, chan);
228         }
229
230         ModResult OnCheckLimit(User* user, Channel* chan) CXX11_OVERRIDE
231         {
232                 // Hook only runs when being invited bypasses +bkl
233                 return IsInvited(user, chan);
234         }
235
236         ModResult OnCheckInvite(User* user, Channel* chan) CXX11_OVERRIDE
237         {
238                 // Hook always runs
239                 return IsInvited(user, chan);
240         }
241
242         void OnUserDisconnect(LocalUser* user) CXX11_OVERRIDE
243         {
244                 invapi.RemoveAll(user);
245         }
246
247         void OnChannelDelete(Channel* chan) CXX11_OVERRIDE
248         {
249                 // Make sure the channel won't appear in invite lists from now on, don't wait for cull to unset the ext
250                 invapi.RemoveAll(chan);
251         }
252
253         ModResult OnCheckExemption(User* user, Channel* chan, const std::string& restriction) CXX11_OVERRIDE
254         {
255                 if (!exemptions.count(restriction))
256                         return MOD_RES_PASSTHRU;
257
258                 unsigned int mypfx = chan->GetPrefixValue(user);
259                 char minmode = exemptions[restriction];
260
261                 PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(minmode);
262                 if (mh && mypfx >= mh->GetPrefixRank())
263                         return MOD_RES_ALLOW;
264                 if (mh || minmode == '*')
265                         return MOD_RES_DENY;
266                 return MOD_RES_PASSTHRU;
267         }
268
269         void Prioritize() CXX11_OVERRIDE
270         {
271                 ServerInstance->Modules.SetPriority(this, I_OnPostJoin, PRIORITY_FIRST);
272                 ServerInstance->Modules.SetPriority(this, I_OnUserPreJoin, PRIORITY_LAST);
273         }
274
275         Version GetVersion() CXX11_OVERRIDE
276         {
277                 return Version("Provides the INVITE, JOIN, KICK, NAMES, and TOPIC commands", VF_VENDOR|VF_CORE);
278         }
279 };
280
281 MODULE_INIT(CoreModChannel)