Send multiline CAP LS response for IRCv3.2 clients

This commit is contained in:
Alexey Sokolov
2023-01-08 15:32:27 +00:00
parent f2ebee26b7
commit b57d794aaf
3 changed files with 78 additions and 6 deletions
+2
View File
@@ -103,6 +103,7 @@ class CClient : public CIRCSocket {
m_bGotPass(false),
m_bGotNick(false),
m_bGotUser(false),
m_bCap302(false),
m_bInCap(false),
m_bCapNotify(false),
m_bAwayNotify(false),
@@ -349,6 +350,7 @@ class CClient : public CIRCSocket {
bool m_bGotPass;
bool m_bGotNick;
bool m_bGotUser;
bool m_bCap302;
bool m_bInCap;
bool m_bCapNotify;
bool m_bAwayNotify;
+34 -6
View File
@@ -687,6 +687,21 @@ void CClient::RespondCap(const CString& sResponse) {
PutClient(":irc.znc.in CAP " + GetNick() + " " + sResponse);
}
static VCString MultiLine(const SCString& ssCaps) {
VCString vsRes = {""};
for (const CString& sCap : ssCaps) {
if (vsRes.back().length() + sCap.length() > 400) {
vsRes.push_back(sCap);
} else {
if (!vsRes.back().empty()) {
vsRes.back() += " ";
}
vsRes.back() += sCap;
}
}
return vsRes;
}
void CClient::HandleCap(const CMessage& Message) {
CString sSubCmd = Message.GetParam(0);
@@ -699,12 +714,18 @@ void CClient::HandleCap(const CMessage& Message) {
ssOfferCaps.insert(it.first);
}
GLOBALMODULECALL(OnClientCapLs(this, ssOfferCaps), NOTHING);
CString sRes =
CString(" ").Join(ssOfferCaps.begin(), ssOfferCaps.end());
RespondCap("LS :" + sRes);
VCString vsCaps = MultiLine(ssOfferCaps);
m_bInCap = true;
if (Message.GetParam(1).ToInt() >= 302) {
m_bCap302 = true;
m_bCapNotify = true;
for (int i = 0; i < vsCaps.size() - 1; ++i) {
RespondCap("LS * :" + vsCaps[i]);
}
RespondCap("LS :" + vsCaps.back());
} else {
// Can't send more than one line of caps :(
RespondCap("LS :" + vsCaps.front());
}
} else if (sSubCmd.Equals("END")) {
m_bInCap = false;
@@ -763,9 +784,16 @@ void CClient::HandleCap(const CMessage& Message) {
RespondCap("ACK :" + Message.GetParam(1));
} else if (sSubCmd.Equals("LIST")) {
CString sList =
CString(" ").Join(m_ssAcceptedCaps.begin(), m_ssAcceptedCaps.end());
RespondCap("LIST :" + sList);
VCString vsCaps = MultiLine(m_ssAcceptedCaps);
if (m_bCap302) {
for (int i = 0; i < vsCaps.size() - 1; ++i) {
RespondCap("LIST * :" + vsCaps[i]);
}
RespondCap("LIST :" + vsCaps.back());
} else {
// Can't send more than one line of caps :(
RespondCap("LISTS :" + vsCaps.front());
}
} else {
PutClient(":irc.znc.in 410 " + GetNick() + " " + sSubCmd +
" :Invalid CAP subcommand");
+42
View File
@@ -165,6 +165,7 @@ TEST_F(ZNCTest, InvalidConfigInChan) {
auto znc = Run();
znc->ShouldFinishItself(1);
}
TEST_F(ZNCTest, Encoding) {
auto znc = Run();
auto ircd = ConnectIRCd();
@@ -430,5 +431,46 @@ TEST_F(ZNCTest, DenyOptions) {
client2.ReadUntil("Access denied!");
}
TEST_F(ZNCTest, CAP302MultiLS) {
auto znc = Run();
auto ircd = ConnectIRCd();
auto client = LoginClient();
InstallModule("testmod.cpp", R"(
#include <znc/Client.h>
#include <znc/Modules.h>
class TestModule : public CModule {
public:
MODCONSTRUCTOR(TestModule) {}
void OnClientCapLs(CClient* pClient, SCString& ssCaps) override {
for (int i = 0; i < 100; ++i) {
ssCaps.insert("testcap-" + CString(i));
}
}
};
GLOBALMODULEDEFS(TestModule, "Test")
)");
client.Write("znc loadmod testmod");
client.ReadUntil("Loaded module testmod");
auto client2 = ConnectClient();
client2.Write("CAP LS");
client2.ReadUntil("LS :");
auto rem = client2.ReadRemainder();
ASSERT_GT(rem.indexOf("testcap-10"), 10);
ASSERT_EQ(rem.indexOf("testcap-80"), -1);
ASSERT_EQ(rem.indexOf("LS"), -1);
client2 = ConnectClient();
client2.Write("CAP LS 302");
client2.ReadUntil("LS * :");
rem = client2.ReadRemainder();
qsizetype w = 0;
ASSERT_GT(w = rem.indexOf("testcap-10"), 1);
ASSERT_GT(w = rem.indexOf("testcap-22", w), 1);
ASSERT_GT(w = rem.indexOf("LS * :", w), 1);
ASSERT_GT(rem.indexOf("testcap-80", w), 1);
ASSERT_GT(rem.indexOf("LS :", w), 1);
}
} // namespace
} // namespace znc_inttest