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