#include "modules/reload.h"
#include "modules/cap.h"
+enum
+{
+ // From IRCv3 capability-negotiation-3.1.
+ ERR_INVALIDCAPCMD = 410
+};
+
namespace Cap
{
class ManagerImpl;
static bool CanRequest(LocalUser* user, Ext usercaps, Capability* cap, bool adding)
{
- if ((usercaps & cap->GetMask()) == adding)
+ const bool hascap = ((usercaps & cap->GetMask()) != 0);
+ if (hascap == adding)
return true;
return cap->OnRequest(user, adding);
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));
}
cap->set(user, false);
}
+ ServerInstance->Modules.DelReferent(cap);
cap->Unregister();
caps.erase(cap->GetName());
}
}
};
+namespace
+{
+ std::string SerializeCaps(const Extensible* container, void* item, bool human)
+ {
+ // 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 (!user)
+ return std::string();
+
+ // List requested caps
+ std::string ret;
+ 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.
+ Cap::Protocol protocol = managerimpl->GetProtocol(user);
+ if (human)
+ ret.append("capversion=3.");
+ else if (!ret.empty())
+ ret.erase(ret.length()-1);
+
+ if (protocol == Cap::CAP_302)
+ ret.push_back('2');
+ else
+ ret.push_back('1');
+
+ return ret;
+ }
+}
+
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 Cap::ExtItem::ToHuman(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;
+ return SerializeCaps(container, item, true);
}
-void Cap::ExtItem::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+std::string Cap::ExtItem::ToInternal(const Extensible* container, void* item) const
{
- if (format == FORMAT_NETWORK)
- return;
+ return SerializeCaps(container, item, false);
+}
+void Cap::ExtItem::FromInternal(Extensible* container, const std::string& value)
+{
LocalUser* user = IS_LOCAL(static_cast<User*>(container));
if (!user)
return; // Can't happen
managerimpl->HandleReq(user, caplist);
}
+class CapMessage : public Cap::MessageBase
+{
+ public:
+ CapMessage(LocalUser* user, const std::string& subcmd, const std::string& result)
+ : Cap::MessageBase(subcmd)
+ {
+ SetUser(user);
+ PushParamRef(result);
+ }
+};
+
class CommandCap : public SplitCommand
{
Events::ModuleEventProvider evprov;
Cap::ManagerImpl manager;
+ ClientProtocol::EventProvider protoevprov;
- static void DisplayResult(LocalUser* user, std::string& result)
+ void DisplayResult(LocalUser* user, const std::string& subcmd, std::string& result)
{
- if (result.size() > 5)
+ if (*result.rbegin() == ' ')
result.erase(result.end()-1);
- user->WriteCommand("CAP", result);
+ DisplayResult2(user, subcmd, result);
+ }
+
+ void DisplayResult2(LocalUser* user, const std::string& subcmd, const std::string& result)
+ {
+ CapMessage msg(user, subcmd, result);
+ ClientProtocol::Event ev(protoevprov, msg);
+ user->Send(ev);
}
public:
: SplitCommand(mod, "CAP", 1)
, evprov(mod, "event/cap")
, manager(mod, evprov)
+ , protoevprov(mod, name)
, holdext("cap_hold", ExtensionItem::EXT_USER, mod)
{
works_before_reg = true;
}
- CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user) CXX11_OVERRIDE
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
{
if (user->registered != REG_ALL)
holdext.set(user, 1);
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);
+ const std::string replysubcmd = (manager.HandleReq(user, parameters[1]) ? "ACK" : "NAK");
+ DisplayResult2(user, replysubcmd, parameters[1]);
}
else if (subcommand == "END")
{
}
else if ((subcommand == "LS") || (subcommand == "LIST"))
{
+ Cap::Protocol capversion = Cap::CAP_LEGACY;
const bool is_ls = (subcommand.length() == 2);
- if ((is_ls) && (parameters.size() > 1) && (parameters[1] == "302"))
- manager.Set302Protocol(user);
+ if ((is_ls) && (parameters.size() > 1))
+ {
+ unsigned int version = ConvToNum<unsigned int>(parameters[1]);
+ if (version >= 302)
+ {
+ capversion = Cap::CAP_302;
+ manager.Set302Protocol(user);
+ }
+ }
- std::string result = subcommand + " :";
+ std::string result;
// Show values only if supports v3.2 and doing LS
- manager.HandleList(result, user, is_ls, ((is_ls) && (manager.GetProtocol(user) != Cap::CAP_LEGACY)));
- DisplayResult(user, result);
+ manager.HandleList(result, user, is_ls, ((is_ls) && (capversion != Cap::CAP_LEGACY)));
+ DisplayResult(user, subcommand, result);
}
else if ((subcommand == "CLEAR") && (manager.GetProtocol(user) == Cap::CAP_LEGACY))
{
- std::string result = "ACK :";
+ std::string result;
manager.HandleClear(user, result);
- DisplayResult(user, result);
+ DisplayResult(user, "ACK", result);
}
else
{
- user->WriteNumeric(ERR_INVALIDCAPSUBCOMMAND, "%s :Invalid CAP subcommand", subcommand.c_str());
+ user->WriteNumeric(ERR_INVALIDCAPCMD, subcommand.empty() ? "*" : subcommand, "Invalid CAP subcommand");
return CMD_FAILURE;
}