+ if (show_values)
+ {
+ const std::string* capvalue = cap->GetValue(user);
+ if ((capvalue) && (!capvalue->empty()) && (capvalue->find(' ') == std::string::npos))
+ {
+ token.push_back('=');
+ token.append(*capvalue, 0, MAX_VALUE_LENGTH);
+ }
+ }
+ out.push_back(token);
+ }
+ }
+
+ void HandleClear(LocalUser* user, std::vector<std::string>& result)
+ {
+ HandleList(result, user, false, false, true);
+ capext.unset(user);
+ }
+};
+
+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::vector<std::string> result;
+ managerimpl->HandleList(result, 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.
+ std::string version;
+ if (human)
+ version.append("capversion=3.");
+ switch (managerimpl->GetProtocol(user))
+ {
+ case Cap::CAP_302:
+ version.push_back('2');
+ break;
+ default:
+ version.push_back('1');
+ break;
+ }
+ result.push_back(version);
+
+ return stdalgo::string::join(result, ' ');
+ }
+}
+
+Cap::ExtItem::ExtItem(Module* mod)
+ : LocalIntExt("caps", ExtensionItem::EXT_USER, mod)
+{
+}
+
+std::string Cap::ExtItem::ToHuman(const Extensible* container, void* item) const
+{
+ return SerializeCaps(container, item, true);
+}
+
+std::string Cap::ExtItem::ToInternal(const Extensible* container, void* item) const
+{
+ 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
+
+ // 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 CapMessage : public Cap::MessageBase
+{
+ public:
+ CapMessage(LocalUser* user, const std::string& subcmd, const std::string& result, bool asterisk)
+ : Cap::MessageBase(subcmd)
+ {
+ SetUser(user);
+ if (asterisk)
+ PushParam("*");
+ PushParamRef(result);
+ }
+};
+
+class CommandCap : public SplitCommand
+{
+ private:
+ Events::ModuleEventProvider evprov;
+ Cap::ManagerImpl manager;
+ ClientProtocol::EventProvider protoevprov;
+
+ void DisplayResult(LocalUser* user, const std::string& subcmd, std::vector<std::string> result, bool asterisk)
+ {
+ size_t maxline = ServerInstance->Config->Limits.MaxLine - ServerInstance->Config->ServerName.size() - user->nick.length() - subcmd.length() - 11;
+ std::string line;
+ for (std::vector<std::string>::const_iterator iter = result.begin(); iter != result.end(); ++iter)
+ {
+ if (line.length() + iter->length() < maxline)
+ {
+ line.append(*iter);
+ line.push_back(' ');
+ }
+ else
+ {
+ DisplaySingleResult(user, subcmd, line, asterisk);
+ line.clear();
+ }
+ }
+ DisplaySingleResult(user, subcmd, line, false);
+ }
+
+ void DisplaySingleResult(LocalUser* user, const std::string& subcmd, const std::string& result, bool asterisk)
+ {
+ CapMessage msg(user, subcmd, result, asterisk);
+ ClientProtocol::Event ev(protoevprov, msg);
+ user->Send(ev);
+ }
+
+ public:
+ LocalIntExt holdext;
+
+ CommandCap(Module* mod)
+ : 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(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ if (user->registered != REG_ALL)
+ holdext.set(user, 1);
+
+ const std::string& subcommand = parameters[0];
+ if (irc::equals(subcommand, "REQ"))
+ {
+ if (parameters.size() < 2)
+ return CMD_FAILURE;
+
+ const std::string replysubcmd = (manager.HandleReq(user, parameters[1]) ? "ACK" : "NAK");
+ DisplaySingleResult(user, replysubcmd, parameters[1], false);
+ }
+ else if (irc::equals(subcommand, "END"))
+ {
+ holdext.unset(user);
+ }
+ else if (irc::equals(subcommand, "LS") || irc::equals(subcommand, "LIST"))
+ {
+ Cap::Protocol capversion = Cap::CAP_LEGACY;
+ const bool is_ls = (subcommand.length() == 2);
+ 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::vector<std::string> result;
+ // Show values only if supports v3.2 and doing LS
+ manager.HandleList(result, user, is_ls, ((is_ls) && (capversion != Cap::CAP_LEGACY)));
+ DisplayResult(user, subcommand, result, (capversion != Cap::CAP_LEGACY));
+ }
+ else if (irc::equals(subcommand, "CLEAR") && (manager.GetProtocol(user) == Cap::CAP_LEGACY))
+ {
+ std::vector<std::string> result;
+ manager.HandleClear(user, result);
+ DisplayResult(user, "ACK", result, false);