summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAttila Molnar <attilamolnar@hush.com>2015-12-05 15:29:01 +0100
committerAttila Molnar <attilamolnar@hush.com>2015-12-05 15:29:01 +0100
commit259b1113944a01aeb450265f03fb97a283e8ef15 (patch)
treea8f9bc4bba98bca09a2ddfa4e9bee525d566fca0 /src
parent806f48627913db210be7c9104de5afe2f47bdb33 (diff)
Add rewritten m_cap module
- Caps are now managed by m_cap - Each cap uses one bit in an extension item shared with other caps
Diffstat (limited to 'src')
-rw-r--r--src/modules/m_cap.cpp245
-rw-r--r--src/modules/m_ircv3.cpp16
-rw-r--r--src/modules/m_namesx.cpp8
-rw-r--r--src/modules/m_sasl.cpp8
-rw-r--r--src/modules/m_starttls.cpp2
-rw-r--r--src/modules/m_uhnames.cpp8
6 files changed, 266 insertions, 21 deletions
diff --git a/src/modules/m_cap.cpp b/src/modules/m_cap.cpp
new file mode 100644
index 000000000..9e857ad28
--- /dev/null
+++ b/src/modules/m_cap.cpp
@@ -0,0 +1,245 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/cap.h"
+
+namespace Cap
+{
+ class ManagerImpl;
+}
+
+class Cap::ManagerImpl : public Cap::Manager
+{
+ typedef insp::flat_map<std::string, Capability*, irc::insensitive_swo> CapMap;
+
+ ExtItem capext;
+ CapMap caps;
+
+ Capability::Bit AllocateBit() const
+ {
+ Capability::Bit used = 0;
+ for (CapMap::const_iterator i = caps.begin(); i != caps.end(); ++i)
+ {
+ Capability* cap = i->second;
+ used |= cap->GetMask();
+ }
+
+ for (unsigned int i = 0; i < MAX_CAPS; i++)
+ {
+ Capability::Bit bit = (1 << i);
+ if (!(used & bit))
+ return bit;
+ }
+ throw ModuleException("Too many caps");
+ }
+
+ public:
+ ManagerImpl(Module* mod)
+ : Cap::Manager(mod)
+ , capext("caps", ExtensionItem::EXT_USER, mod)
+ {
+ }
+
+ ~ManagerImpl()
+ {
+ for (CapMap::iterator i = caps.begin(); i != caps.end(); ++i)
+ {
+ Capability* cap = i->second;
+ cap->Unregister();
+ }
+ }
+
+ void AddCap(Cap::Capability* cap) CXX11_OVERRIDE
+ {
+ // No-op if the cap is already registered.
+ // This allows modules to call SetActive() on a cap without checking if it's active first.
+ if (cap->IsRegistered())
+ return;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Registering cap %s", cap->GetName().c_str());
+ cap->bit = AllocateBit();
+ cap->extitem = &capext;
+ caps.insert(std::make_pair(cap->GetName(), cap));
+ }
+
+ void DelCap(Cap::Capability* cap) CXX11_OVERRIDE
+ {
+ // No-op if the cap is not registered, see AddCap() above
+ if (!cap->IsRegistered())
+ return;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Unregistering cap %s", cap->GetName().c_str());
+
+ // Turn off the cap for all users
+ const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+ for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ LocalUser* user = *i;
+ cap->set(user, false);
+ }
+
+ cap->Unregister();
+ caps.erase(cap->GetName());
+ }
+
+ Capability* Find(const std::string& capname) const CXX11_OVERRIDE
+ {
+ CapMap::const_iterator it = caps.find(capname);
+ if (it != caps.end())
+ return it->second;
+ return NULL;
+ }
+
+ bool HandleReq(LocalUser* user, const std::string& reqlist)
+ {
+ Ext usercaps = capext.get(user);
+ irc::spacesepstream ss(reqlist);
+ for (std::string capname; ss.GetToken(capname); )
+ {
+ bool remove = (capname[0] == '-');
+ if (remove)
+ capname.erase(capname.begin());
+
+ Capability* cap = ManagerImpl::Find(capname);
+ if (!cap)
+ return false;
+
+ if (remove)
+ usercaps = cap->DelFromMask(usercaps);
+ else
+ usercaps = cap->AddToMask(usercaps);
+ }
+
+ capext.set(user, usercaps);
+ return true;
+ }
+
+ void HandleList(std::string& out, LocalUser* user, bool show_all, bool minus_prefix = false) const
+ {
+ Ext show_caps = (show_all ? ~0 : capext.get(user));
+
+ for (CapMap::const_iterator i = caps.begin(); i != caps.end(); ++i)
+ {
+ Capability* cap = i->second;
+ if (!(show_caps & cap->GetMask()))
+ continue;
+
+ if (minus_prefix)
+ out.push_back('-');
+ out.append(cap->GetName()).push_back(' ');
+ }
+ }
+
+ void HandleClear(LocalUser* user, std::string& result)
+ {
+ HandleList(result, user, false, true);
+ capext.unset(user);
+ }
+};
+
+class CommandCap : public SplitCommand
+{
+ Cap::ManagerImpl manager;
+
+ static void DisplayResult(LocalUser* user, std::string& result)
+ {
+ if (result.size() > 5)
+ result.erase(result.end()-1);
+ user->WriteCommand("CAP", result);
+ }
+
+ public:
+ LocalIntExt holdext;
+
+ CommandCap(Module* mod)
+ : SplitCommand(mod, "CAP", 1)
+ , manager(mod)
+ , holdext("cap_hold", ExtensionItem::EXT_USER, mod)
+ {
+ works_before_reg = true;
+ }
+
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user) CXX11_OVERRIDE
+ {
+ if (user->registered != REG_ALL)
+ holdext.set(user, 1);
+
+ std::string subcommand(parameters[0].length(), ' ');
+ std::transform(parameters[0].begin(), parameters[0].end(), subcommand.begin(), ::toupper);
+
+ if (subcommand == "REQ")
+ {
+ if (parameters.size() < 2)
+ return CMD_FAILURE;
+
+ std::string result = (manager.HandleReq(user, parameters[1]) ? "ACK :" : "NAK :");
+ result.append(parameters[1]);
+ user->WriteCommand("CAP", result);
+ }
+ else if (subcommand == "END")
+ {
+ holdext.unset(user);
+ }
+ else if ((subcommand == "LS") || (subcommand == "LIST"))
+ {
+ const bool is_ls = (subcommand.length() == 2);
+
+ std::string result = subcommand + " :";
+ manager.HandleList(result, user, is_ls);
+ DisplayResult(user, result);
+ }
+ else if (subcommand == "CLEAR")
+ {
+ std::string result = "ACK :";
+ manager.HandleClear(user, result);
+ DisplayResult(user, result);
+ }
+ else
+ {
+ user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, "%s :Invalid CAP subcommand", subcommand.c_str());
+ return CMD_FAILURE;
+ }
+
+ return CMD_SUCCESS;
+ }
+};
+
+class ModuleCap : public Module
+{
+ CommandCap cmd;
+
+ public:
+ ModuleCap()
+ : cmd(this)
+ {
+ }
+
+ ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
+ {
+ return (cmd.holdext.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU);
+ }
+
+ Version GetVersion() CXX11_OVERRIDE
+ {
+ return Version("Provides support for CAP capability negotiation", VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleCap)
diff --git a/src/modules/m_ircv3.cpp b/src/modules/m_ircv3.cpp
index 123d07069..c99d920ba 100644
--- a/src/modules/m_ircv3.cpp
+++ b/src/modules/m_ircv3.cpp
@@ -22,7 +22,7 @@
class WriteNeighborsWithCap : public User::ForEachNeighborHandler
{
- const LocalIntExt& cap;
+ const Cap::Capability& cap;
const std::string& msg;
void Execute(LocalUser* user) CXX11_OVERRIDE
@@ -32,8 +32,8 @@ class WriteNeighborsWithCap : public User::ForEachNeighborHandler
}
public:
- WriteNeighborsWithCap(User* user, const std::string& message, const GenericCap& capability)
- : cap(capability.ext)
+ WriteNeighborsWithCap(User* user, const std::string& message, const Cap::Capability& capability)
+ : cap(capability)
, msg(message)
{
user->ForEachNeighbor(*this, false);
@@ -42,9 +42,9 @@ class WriteNeighborsWithCap : public User::ForEachNeighborHandler
class ModuleIRCv3 : public Module, public AccountEventListener
{
- GenericCap cap_accountnotify;
- GenericCap cap_awaynotify;
- GenericCap cap_extendedjoin;
+ Cap::Capability cap_accountnotify;
+ Cap::Capability cap_awaynotify;
+ Cap::Capability cap_extendedjoin;
CUList last_excepts;
@@ -105,7 +105,7 @@ class ModuleIRCv3 : public Module, public AccountEventListener
{
// Send the extended join line if the current member is local, has the extended-join cap and isn't excepted
User* member = IS_LOCAL(it->first);
- if ((member) && (cap_extendedjoin.ext.get(member)) && (excepts.find(member) == excepts.end()))
+ if ((member) && (cap_extendedjoin.get(member)) && (excepts.find(member) == excepts.end()))
{
// Construct the lines we're going to send if we haven't constructed them already
if (line.empty())
@@ -179,7 +179,7 @@ class ModuleIRCv3 : public Module, public AccountEventListener
{
// Send the away notify line if the current member is local, has the away-notify cap and isn't excepted
User* member = IS_LOCAL(it->first);
- if ((member) && (cap_awaynotify.ext.get(member)) && (last_excepts.find(member) == last_excepts.end()))
+ if ((member) && (cap_awaynotify.get(member)) && (last_excepts.find(member) == last_excepts.end()))
{
member->Write(line);
}
diff --git a/src/modules/m_namesx.cpp b/src/modules/m_namesx.cpp
index c701f16bf..c906322bf 100644
--- a/src/modules/m_namesx.cpp
+++ b/src/modules/m_namesx.cpp
@@ -25,7 +25,7 @@
class ModuleNamesX : public Module
{
- GenericCap cap;
+ Cap::Capability cap;
public:
ModuleNamesX() : cap(this, "multi-prefix")
{
@@ -52,7 +52,7 @@ class ModuleNamesX : public Module
{
if ((parameters.size()) && (!strcasecmp(parameters[0].c_str(),"NAMESX")))
{
- cap.ext.set(user, 1);
+ cap.set(user, true);
return MOD_RES_DENY;
}
}
@@ -61,7 +61,7 @@ class ModuleNamesX : public Module
ModResult OnNamesListItem(User* issuer, Membership* memb, std::string& prefixes, std::string& nick) CXX11_OVERRIDE
{
- if (cap.ext.get(issuer))
+ if (cap.get(issuer))
prefixes = memb->GetAllPrefixChars();
return MOD_RES_PASSTHRU;
@@ -69,7 +69,7 @@ class ModuleNamesX : public Module
void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, std::string& line) CXX11_OVERRIDE
{
- if ((!memb) || (!cap.ext.get(source)))
+ if ((!memb) || (!cap.get(source)))
return;
// Channel names can contain ":", and ":" as a 'start-of-token' delimiter is
diff --git a/src/modules/m_sasl.cpp b/src/modules/m_sasl.cpp
index 341b3aea7..297abad85 100644
--- a/src/modules/m_sasl.cpp
+++ b/src/modules/m_sasl.cpp
@@ -179,8 +179,8 @@ class CommandAuthenticate : public Command
{
public:
SimpleExtItem<SaslAuthenticator>& authExt;
- GenericCap& cap;
- CommandAuthenticate(Module* Creator, SimpleExtItem<SaslAuthenticator>& ext, GenericCap& Cap)
+ Cap::Capability& cap;
+ CommandAuthenticate(Module* Creator, SimpleExtItem<SaslAuthenticator>& ext, Cap::Capability& Cap)
: Command(Creator, "AUTHENTICATE", 1), authExt(ext), cap(Cap)
{
works_before_reg = true;
@@ -191,7 +191,7 @@ class CommandAuthenticate : public Command
/* Only allow AUTHENTICATE on unregistered clients */
if (user->registered != REG_ALL)
{
- if (!cap.ext.get(user))
+ if (!cap.get(user))
return CMD_FAILURE;
SaslAuthenticator *sasl = authExt.get(user);
@@ -247,7 +247,7 @@ class CommandSASL : public Command
class ModuleSASL : public Module
{
SimpleExtItem<SaslAuthenticator> authExt;
- GenericCap cap;
+ Cap::Capability cap;
CommandAuthenticate auth;
CommandSASL sasl;
Events::ModuleEventProvider sasleventprov;
diff --git a/src/modules/m_starttls.cpp b/src/modules/m_starttls.cpp
index b05302fa9..a47480728 100644
--- a/src/modules/m_starttls.cpp
+++ b/src/modules/m_starttls.cpp
@@ -80,7 +80,7 @@ class CommandStartTLS : public SplitCommand
class ModuleStartTLS : public Module
{
CommandStartTLS starttls;
- GenericCap tls;
+ Cap::Capability tls;
dynamic_reference_nocheck<IOHookProvider> ssl;
public:
diff --git a/src/modules/m_uhnames.cpp b/src/modules/m_uhnames.cpp
index 90bac54f5..ce9c517f4 100644
--- a/src/modules/m_uhnames.cpp
+++ b/src/modules/m_uhnames.cpp
@@ -24,9 +24,9 @@
class ModuleUHNames : public Module
{
- public:
- GenericCap cap;
+ Cap::Capability cap;
+ public:
ModuleUHNames() : cap(this, "userhost-in-names")
{
}
@@ -52,7 +52,7 @@ class ModuleUHNames : public Module
{
if ((parameters.size()) && (!strcasecmp(parameters[0].c_str(),"UHNAMES")))
{
- cap.ext.set(user, 1);
+ cap.set(user, true);
return MOD_RES_DENY;
}
}
@@ -61,7 +61,7 @@ class ModuleUHNames : public Module
ModResult OnNamesListItem(User* issuer, Membership* memb, std::string& prefixes, std::string& nick) CXX11_OVERRIDE
{
- if (cap.ext.get(issuer))
+ if (cap.get(issuer))
nick = memb->user->GetFullHost();
return MOD_RES_PASSTHRU;