]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_cap.cpp
Convert WriteNumeric() calls to pass the parameters of the numeric as method parameters
[user/henk/code/inspircd.git] / src / modules / m_cap.cpp
index e1593e33f50f54f64a870d814346721a2fca68ac..e6e01570d83153a0aff3410041f9cca2a1a82260 100644 (file)
@@ -18,6 +18,7 @@
 
 
 #include "inspircd.h"
+#include "modules/reload.h"
 #include "modules/cap.h"
 
 namespace Cap
@@ -25,8 +26,27 @@ namespace Cap
        class ManagerImpl;
 }
 
-class Cap::ManagerImpl : public Cap::Manager
+static Cap::ManagerImpl* managerimpl;
+
+class Cap::ManagerImpl : public Cap::Manager, public ReloadModule::EventListener
 {
+       /** Stores the cap state of a module being reloaded
+        */
+       struct CapModData
+       {
+               struct Data
+               {
+                       std::string name;
+                       std::vector<std::string> users;
+
+                       Data(Capability* cap)
+                               : name(cap->GetName())
+                       {
+                       }
+               };
+               std::vector<Data> caps;
+       };
+
        typedef insp::flat_map<std::string, Capability*, irc::insensitive_swo> CapMap;
 
        ExtItem capext;
@@ -59,12 +79,75 @@ class Cap::ManagerImpl : public Cap::Manager
                throw ModuleException("Too many caps");
        }
 
+       void OnReloadModuleSave(Module* mod, ReloadModule::CustomData& cd) CXX11_OVERRIDE
+       {
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "OnReloadModuleSave()");
+               if (mod == creator)
+                       return;
+
+               CapModData* capmoddata = new CapModData;
+               cd.add(this, capmoddata);
+
+               for (CapMap::iterator i = caps.begin(); i != caps.end(); ++i)
+               {
+                       Capability* cap = i->second;
+                       // Only save users of caps that belong to the module being reloaded
+                       if (cap->creator != mod)
+                               continue;
+
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Module being reloaded implements cap %s, saving cap users", cap->GetName().c_str());
+                       capmoddata->caps.push_back(CapModData::Data(cap));
+                       CapModData::Data& capdata = capmoddata->caps.back();
+
+                       // Populate list with uuids of users who are using the cap
+                       const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
+                       for (UserManager::LocalList::const_iterator j = list.begin(); j != list.end(); ++j)
+                       {
+                               LocalUser* user = *j;
+                               if (cap->get(user))
+                                       capdata.users.push_back(user->uuid);
+                       }
+               }
+       }
+
+       void OnReloadModuleRestore(Module* mod, void* data) CXX11_OVERRIDE
+       {
+               CapModData* capmoddata = static_cast<CapModData*>(data);
+               for (std::vector<CapModData::Data>::const_iterator i = capmoddata->caps.begin(); i != capmoddata->caps.end(); ++i)
+               {
+                       const CapModData::Data& capdata = *i;
+                       Capability* cap = ManagerImpl::Find(capdata.name);
+                       if (!cap)
+                       {
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Cap %s is no longer available after reload", capdata.name.c_str());
+                               continue;
+                       }
+
+                       // Set back the cap for all users who were using it before the reload
+                       for (std::vector<std::string>::const_iterator j = capdata.users.begin(); j != capdata.users.end(); ++j)
+                       {
+                               const std::string& uuid = *j;
+                               User* user = ServerInstance->FindUUID(uuid);
+                               if (!user)
+                               {
+                                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone when trying to restore cap %s", uuid.c_str(), capdata.name.c_str());
+                                       continue;
+                               }
+
+                               cap->set(user, true);
+                       }
+               }
+               delete capmoddata;
+       }
+
  public:
        ManagerImpl(Module* mod, Events::ModuleEventProvider& evprovref)
                : Cap::Manager(mod)
-               , capext("caps", ExtensionItem::EXT_USER, mod)
+               , ReloadModule::EventListener(mod)
+               , capext(mod)
                , evprov(evprovref)
        {
+               managerimpl = this;
        }
 
        ~ManagerImpl()
@@ -87,6 +170,7 @@ class Cap::ManagerImpl : public Cap::Manager
                cap->bit = AllocateBit();
                cap->extitem = &capext;
                caps.insert(std::make_pair(cap->GetName(), cap));
+               ServerInstance->Modules.AddReferent("cap/" + cap->GetName(), cap);
 
                FOREACH_MOD_CUSTOM(evprov, Cap::EventListener, OnCapAddDel, (cap, true));
        }
@@ -110,6 +194,7 @@ class Cap::ManagerImpl : public Cap::Manager
                        cap->set(user, false);
                }
 
+               ServerInstance->Modules.DelReferent(cap);
                cap->Unregister();
                caps.erase(cap->GetName());
        }
@@ -199,6 +284,56 @@ class Cap::ManagerImpl : public Cap::Manager
        }
 };
 
+Cap::ExtItem::ExtItem(Module* mod)
+       : LocalIntExt("caps", ExtensionItem::EXT_USER, mod)
+{
+}
+
+std::string Cap::ExtItem::serialize(SerializeFormat format, const Extensible* container, void* item) const
+{
+       std::string ret;
+       // XXX: Cast away the const because IS_LOCAL() doesn't handle it
+       LocalUser* user = IS_LOCAL(const_cast<User*>(static_cast<const User*>(container)));
+       if ((format == FORMAT_NETWORK) || (!user))
+               return ret;
+
+       // List requested caps
+       managerimpl->HandleList(ret, user, false, false);
+
+       // Serialize cap protocol version. If building a human-readable string append a new token, otherwise append only a single character indicating the version.
+       Protocol protocol = managerimpl->GetProtocol(user);
+       if (format == FORMAT_USER)
+               ret.append("capversion=3.");
+       else if (!ret.empty())
+               ret.erase(ret.length()-1);
+
+       if (protocol == CAP_302)
+               ret.push_back('2');
+       else
+               ret.push_back('1');
+
+       return ret;
+}
+
+void Cap::ExtItem::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+{
+       if (format == FORMAT_NETWORK)
+               return;
+
+       LocalUser* user = IS_LOCAL(static_cast<User*>(container));
+       if (!user)
+               return; // Can't happen
+
+       // Process the cap protocol version which is a single character at the end of the serialized string
+       const char verchar = *value.rbegin();
+       if (verchar == '2')
+               managerimpl->Set302Protocol(user);
+
+       // Remove the version indicator from the string passed to HandleReq
+       std::string caplist(value, 0, value.size()-1);
+       managerimpl->HandleReq(user, caplist);
+}
+
 class CommandCap : public SplitCommand
 {
        Events::ModuleEventProvider evprov;
@@ -206,7 +341,7 @@ class CommandCap : public SplitCommand
 
        static void DisplayResult(LocalUser* user, std::string& result)
        {
-               if (result.size() > 5)
+               if (*result.rbegin() == ' ')
                        result.erase(result.end()-1);
                user->WriteCommand("CAP", result);
        }
@@ -263,7 +398,7 @@ class CommandCap : public SplitCommand
                }
                else
                {
-                       user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, "%s :Invalid CAP subcommand", subcommand.c_str());
+                       user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, subcommand, "Invalid CAP subcommand");
                        return CMD_FAILURE;
                }