]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_cap.cpp
m_cap Reject CAP CLEAR from clients newer than 3.1
[user/henk/code/inspircd.git] / src / modules / m_cap.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 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 "modules/cap.h"
22
23 namespace Cap
24 {
25         class ManagerImpl;
26 }
27
28 class Cap::ManagerImpl : public Cap::Manager
29 {
30         typedef insp::flat_map<std::string, Capability*, irc::insensitive_swo> CapMap;
31
32         ExtItem capext;
33         CapMap caps;
34
35         static bool CanRequest(LocalUser* user, Ext usercaps, Capability* cap, bool adding)
36         {
37                 if ((usercaps & cap->GetMask()) == adding)
38                         return true;
39
40                 return cap->OnRequest(user, adding);
41         }
42
43         Capability::Bit AllocateBit() const
44         {
45                 Capability::Bit used = 0;
46                 for (CapMap::const_iterator i = caps.begin(); i != caps.end(); ++i)
47                 {
48                         Capability* cap = i->second;
49                         used |= cap->GetMask();
50                 }
51
52                 for (unsigned int i = 0; i < MAX_CAPS; i++)
53                 {
54                         Capability::Bit bit = (1 << i);
55                         if (!(used & bit))
56                                 return bit;
57                 }
58                 throw ModuleException("Too many caps");
59         }
60
61  public:
62         ManagerImpl(Module* mod)
63                 : Cap::Manager(mod)
64                 , capext("caps", ExtensionItem::EXT_USER, mod)
65         {
66         }
67
68         ~ManagerImpl()
69         {
70                 for (CapMap::iterator i = caps.begin(); i != caps.end(); ++i)
71                 {
72                         Capability* cap = i->second;
73                         cap->Unregister();
74                 }
75         }
76
77         void AddCap(Cap::Capability* cap) CXX11_OVERRIDE
78         {
79                 // No-op if the cap is already registered.
80                 // This allows modules to call SetActive() on a cap without checking if it's active first.
81                 if (cap->IsRegistered())
82                         return;
83
84                 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Registering cap %s", cap->GetName().c_str());
85                 cap->bit = AllocateBit();
86                 cap->extitem = &capext;
87                 caps.insert(std::make_pair(cap->GetName(), cap));
88         }
89
90         void DelCap(Cap::Capability* cap) CXX11_OVERRIDE
91         {
92                 // No-op if the cap is not registered, see AddCap() above
93                 if (!cap->IsRegistered())
94                         return;
95
96                 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unregistering cap %s", cap->GetName().c_str());
97
98                 // Turn off the cap for all users
99                 const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
100                 for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
101                 {
102                         LocalUser* user = *i;
103                         cap->set(user, false);
104                 }
105
106                 cap->Unregister();
107                 caps.erase(cap->GetName());
108         }
109
110         Capability* Find(const std::string& capname) const CXX11_OVERRIDE
111         {
112                 CapMap::const_iterator it = caps.find(capname);
113                 if (it != caps.end())
114                         return it->second;
115                 return NULL;
116         }
117
118         Protocol GetProtocol(LocalUser* user) const
119         {
120                 return ((capext.get(user) & CAP_302_BIT) ? CAP_302 : CAP_LEGACY);
121         }
122
123         void Set302Protocol(LocalUser* user)
124         {
125                 capext.set(user, capext.get(user) | CAP_302_BIT);
126         }
127
128         bool HandleReq(LocalUser* user, const std::string& reqlist)
129         {
130                 Ext usercaps = capext.get(user);
131                 irc::spacesepstream ss(reqlist);
132                 for (std::string capname; ss.GetToken(capname); )
133                 {
134                         bool remove = (capname[0] == '-');
135                         if (remove)
136                                 capname.erase(capname.begin());
137
138                         Capability* cap = ManagerImpl::Find(capname);
139                         if ((!cap) || (!CanRequest(user, usercaps, cap, !remove)))
140                                 return false;
141
142                         if (remove)
143                                 usercaps = cap->DelFromMask(usercaps);
144                         else
145                                 usercaps = cap->AddToMask(usercaps);
146                 }
147
148                 capext.set(user, usercaps);
149                 return true;
150         }
151
152         void HandleList(std::string& out, LocalUser* user, bool show_all, bool minus_prefix = false) const
153         {
154                 Ext show_caps = (show_all ? ~0 : capext.get(user));
155
156                 for (CapMap::const_iterator i = caps.begin(); i != caps.end(); ++i)
157                 {
158                         Capability* cap = i->second;
159                         if (!(show_caps & cap->GetMask()))
160                                 continue;
161
162                         if ((show_all) && (!cap->OnList(user)))
163                                 continue;
164
165                         if (minus_prefix)
166                                 out.push_back('-');
167                         out.append(cap->GetName()).push_back(' ');
168                 }
169         }
170
171         void HandleClear(LocalUser* user, std::string& result)
172         {
173                 HandleList(result, user, false, true);
174                 capext.unset(user);
175         }
176 };
177
178 class CommandCap : public SplitCommand
179 {
180         Cap::ManagerImpl manager;
181
182         static void DisplayResult(LocalUser* user, std::string& result)
183         {
184                 if (result.size() > 5)
185                         result.erase(result.end()-1);
186                 user->WriteCommand("CAP", result);
187         }
188
189  public:
190         LocalIntExt holdext;
191
192         CommandCap(Module* mod)
193                 : SplitCommand(mod, "CAP", 1)
194                 , manager(mod)
195                 , holdext("cap_hold", ExtensionItem::EXT_USER, mod)
196         {
197                 works_before_reg = true;
198         }
199
200         CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user) CXX11_OVERRIDE
201         {
202                 if (user->registered != REG_ALL)
203                         holdext.set(user, 1);
204
205                 std::string subcommand(parameters[0].length(), ' ');
206                 std::transform(parameters[0].begin(), parameters[0].end(), subcommand.begin(), ::toupper);
207
208                 if (subcommand == "REQ")
209                 {
210                         if (parameters.size() < 2)
211                                 return CMD_FAILURE;
212
213                         std::string result = (manager.HandleReq(user, parameters[1]) ? "ACK :" : "NAK :");
214                         result.append(parameters[1]);
215                         user->WriteCommand("CAP", result);
216                 }
217                 else if (subcommand == "END")
218                 {
219                         holdext.unset(user);
220                 }
221                 else if ((subcommand == "LS") || (subcommand == "LIST"))
222                 {
223                         const bool is_ls = (subcommand.length() == 2);
224                         if ((is_ls) && (parameters.size() > 1) && (parameters[1] == "302"))
225                                 manager.Set302Protocol(user);
226
227                         std::string result = subcommand + " :";
228                         manager.HandleList(result, user, is_ls);
229                         DisplayResult(user, result);
230                 }
231                 else if ((subcommand == "CLEAR") && (manager.GetProtocol(user) == Cap::CAP_LEGACY))
232                 {
233                         std::string result = "ACK :";
234                         manager.HandleClear(user, result);
235                         DisplayResult(user, result);
236                 }
237                 else
238                 {
239                         user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, "%s :Invalid CAP subcommand", subcommand.c_str());
240                         return CMD_FAILURE;
241                 }
242
243                 return CMD_SUCCESS;
244         }
245 };
246
247 class ModuleCap : public Module
248 {
249         CommandCap cmd;
250
251  public:
252         ModuleCap()
253                 : cmd(this)
254         {
255         }
256
257         ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
258         {
259                 return (cmd.holdext.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU);
260         }
261
262         Version GetVersion() CXX11_OVERRIDE
263         {
264                 return Version("Provides support for CAP capability negotiation", VF_VENDOR);
265         }
266 };
267
268 MODULE_INIT(ModuleCap)