]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Add rewritten m_cap module
authorAttila Molnar <attilamolnar@hush.com>
Sat, 5 Dec 2015 14:29:01 +0000 (15:29 +0100)
committerAttila Molnar <attilamolnar@hush.com>
Sat, 5 Dec 2015 14:29:01 +0000 (15:29 +0100)
- Caps are now managed by m_cap
- Each cap uses one bit in an extension item shared with other caps

include/modules/cap.h [new file with mode: 0644]
src/modules/m_cap.cpp [new file with mode: 0644]
src/modules/m_ircv3.cpp
src/modules/m_namesx.cpp
src/modules/m_sasl.cpp
src/modules/m_starttls.cpp
src/modules/m_uhnames.cpp

diff --git a/include/modules/cap.h b/include/modules/cap.h
new file mode 100644 (file)
index 0000000..9dd44a4
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * 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/>.
+ */
+
+
+#pragma once
+
+#include "event.h"
+
+namespace Cap
+{
+       static const unsigned int MAX_CAPS = sizeof(intptr_t) * 8;
+       typedef intptr_t Ext;
+       typedef LocalIntExt ExtItem;
+       class Capability;
+
+       class Manager : public DataProvider
+       {
+        public:
+               Manager(Module* mod)
+                       : DataProvider(mod, "capmanager")
+               {
+               }
+
+               /** Register a client capability.
+                * Modules should call Capability::SetActive(true) instead of this method.
+                * @param cap Capability to register
+                */
+               virtual void AddCap(Capability* cap) = 0;
+
+               /** Unregister a client capability.
+                * Modules should call Capability::SetActive(false) instead of this method.
+                * @param cap Capability to unregister
+                */
+               virtual void DelCap(Capability* cap) = 0;
+
+               /** Find a capability by name
+                * @param name Capability to find
+                * @return Capability object pointer if found, NULL otherwise
+                */
+               virtual Capability* Find(const std::string& name) const = 0;
+       };
+
+       /** Represents a client capability.
+        *
+        * Capabilities offer extensions to the client to server protocol. They must be negotiated with clients before they have any effect on the protocol.
+        * Each cap must have a unique name that is used during capability negotiation.
+        *
+        * After construction the cap is ready to be used by clients without any further setup, like other InspIRCd services.
+        * The get() method accepts a user as parameter and can be used to check whether that user has negotiated usage of the cap. This is only known for local users.
+        *
+        * The cap module must be loaded for the capability to work. The IsRegistered() method can be used to query whether the cap is actually online or not.
+        * The capability can be deactivated and reactivated with the SetActive() method. Deactivated caps behave as if they don't exist.
+        */
+       class Capability : public ServiceProvider, private dynamic_reference_base::CaptureHook
+       {
+               typedef size_t Bit;
+
+               /** Bit allocated to this cap, undefined if the cap is unregistered
+                */
+               Bit bit;
+
+               /** Extension containing all caps set by a user. NULL if the cap is unregistered.
+                */
+               ExtItem* extitem;
+
+               /** True if the cap is active. Only active caps are registered in the manager.
+                */
+               bool active;
+
+               /** Reference to the cap manager object
+                */
+               dynamic_reference<Manager> manager;
+
+               void OnCapture() CXX11_OVERRIDE
+               {
+                       if (active)
+                               SetActive(true);
+               }
+
+               void Unregister()
+               {
+                       bit = 0;
+                       extitem = NULL;
+               }
+
+               Ext AddToMask(Ext mask) const { return (mask | GetMask()); }
+               Ext DelFromMask(Ext mask) const { return (mask & (~GetMask())); }
+               Bit GetMask() const { return bit; }
+
+               friend class ManagerImpl;
+
+        public:
+               /** Constructor, initializes the capability.
+                * Caps are active by default.
+                * @param mod Module providing the cap
+                * @param Name Raw name of the cap as used in the protocol (CAP LS, etc.)
+                */
+               Capability(Module* mod, const std::string& Name)
+                       : ServiceProvider(mod, Name, SERVICE_CUSTOM)
+                       , active(true)
+                       , manager(mod, "capmanager")
+               {
+                       Unregister();
+               }
+
+               ~Capability()
+               {
+                       SetActive(false);
+               }
+
+               void RegisterService() CXX11_OVERRIDE
+               {
+                       manager.SetCaptureHook(this);
+                       SetActive(true);
+               }
+
+               /** Check whether a user has the capability turned on.
+                * This method is safe to call if the cap is unregistered and will return false.
+                * @param user User to check
+                * @return True if the user is using this capability, false otherwise
+                */
+               bool get(User* user) const
+               {
+                       if (!IsRegistered())
+                               return false;
+                       Ext caps = extitem->get(user);
+                       return (caps & GetMask());
+               }
+
+               /** Turn the capability on/off for a user. If the cap is not registered this method has no effect.
+                * @param user User to turn the cap on/off for
+                * @param val True to turn the cap on, false to turn it off
+                */
+               void set(User* user, bool val)
+               {
+                       if (!IsRegistered())
+                               return;
+                       Ext curr = extitem->get(user);
+                       extitem->set(user, (val ? AddToMask(curr) : DelFromMask(curr)));
+               }
+
+               /** Activate or deactivate the capability.
+                * If activating, the cap is marked as active and if the manager is available the cap is registered in the manager.
+                * If deactivating, the cap is marked as inactive and if it is registered, it will be unregistered.
+                * Users who had the cap turned on will have it turned off automatically.
+                * @param activate True to activate the cap, false to deactivate it
+                */
+               void SetActive(bool activate)
+               {
+                       active = activate;
+                       if (manager)
+                       {
+                               if (activate)
+                                       manager->AddCap(this);
+                               else
+                                       manager->DelCap(this);
+                       }
+               }
+
+               /** Get the name of the capability that's used in the protocol
+                * @return Name of the capability as used in the protocol
+                */
+               const std::string& GetName() const { return name; }
+
+               /** Check whether the capability is active. The cap must be active and registered to be used by users.
+                * @return True if the cap is active, false if it has been deactivated
+                */
+               bool IsActive() const { return active; }
+
+               /** Check whether the capability is registered
+                * The cap must be active and the manager must be available for a cap to be registered.
+                * @return True if the cap is registered in the manager, false otherwise
+                */
+               bool IsRegistered() const { return (extitem != NULL); }
+       };
+}
diff --git a/src/modules/m_cap.cpp b/src/modules/m_cap.cpp
new file mode 100644 (file)
index 0000000..9e857ad
--- /dev/null
@@ -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)
index 123d0706914bc3d10eba68b4a6f79e47fbbad389..c99d920bae297f9fd4d08c561c141ea6e353b426 100644 (file)
@@ -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);
                        }
index c701f16bf1b20cf450e9c75583fa2d9e70ae3575..c906322bf529967fba09f4413bc0e0fef0b8afb3 100644 (file)
@@ -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
index 341b3aea7166139068f75039e4ebc1b101e98bb4..297abad8571017a6d432ff7756428cd99678aec6 100644 (file)
@@ -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;
index b05302fa96c93f83197e4e547e6e67d79c04004a..a47480728c5f5fb1569993b30ad3388b7bf34cc9 100644 (file)
@@ -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:
index 90bac54f58a243635ae15a76ca525e7654a48796..ce9c517f49149dbb3c81ac135561f8ce4b4e8d2e 100644 (file)
@@ -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;