diff --git a/include/znc/Client.h b/include/znc/Client.h index eef95d91..01239cf4 100644 --- a/include/znc/Client.h +++ b/include/znc/Client.h @@ -124,6 +124,7 @@ class CClient : public CIRCSocket { m_sIdentifier(""), m_spAuth(), m_ssAcceptedCaps(), + m_ssSupportedTags(), m_mCoreCaps({ {"multi-prefix", {false, [this](bool bVal) { m_bNamesx = bVal; }}}, @@ -132,8 +133,14 @@ class CClient : public CIRCSocket { {"echo-message", {false, [this](bool bVal) { m_bEchoMessage = bVal; }}}, {"server-time", - {false, [this](bool bVal) { m_bServerTime = bVal; }}}, - {"batch", {false, [this](bool bVal) { m_bBatch = bVal; }}}, + {false, [this](bool bVal) { + m_bServerTime = bVal; + SetTagSupport("time", bVal); + }}}, + {"batch", {false, [this](bool bVal) { + m_bBatch = bVal; + SetTagSupport("batch", bVal); + }}}, {"cap-notify", {false, [this](bool bVal) { m_bCapNotify = bVal; }}}, {"away-notify", @@ -258,6 +265,15 @@ class CClient : public CIRCSocket { return 1 == m_ssAcceptedCaps.count(sCap); } + bool IsTagEnabled(const CString& sTag) const { + return 1 == m_ssSupportedTags.count(sTag); + } + /** Registers a tag as being supported or unsupported by a client. + * @param sTag The tag to register. + * @param bState Whether the client supports the tag. + */ + void SetTagSupport(const CString& sTag, bool bState); + void NotifyServerDependentCaps(const SCString& ssCaps); void ClearServerDependentCaps(); @@ -336,6 +352,7 @@ class CClient : public CIRCSocket { CString m_sIdentifier; std::shared_ptr m_spAuth; SCString m_ssAcceptedCaps; + SCString m_ssSupportedTags; // The capabilities supported by the ZNC core - capability names mapped // to a pair which contains a bool describing whether the capability is // server-dependent, and a capability value change handler. diff --git a/include/znc/Modules.h b/include/znc/Modules.h index 85c0d55a..2dfc5c57 100644 --- a/include/znc/Modules.h +++ b/include/znc/Modules.h @@ -1288,9 +1288,13 @@ class CModule { virtual bool IsClientCapSupported(CClient* pClient, const CString& sCap, bool bState); /** Called when we actually need to turn a capability on or off for a client. + * If implementing a custom capability, make sure to call + * pClient->SetTagSupport("tag-name", bState) for each tag that the + * capability provides. * @param pClient The client which requested the capability. * @param sCap name of wanted capability. * @param bState On or off, depending on which case client needs. + * @see CClient::SetTagSupport() */ virtual void OnClientCapRequest(CClient* pClient, const CString& sCap, bool bState); diff --git a/src/Client.cpp b/src/Client.cpp index db3f0456..ef5e7802 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -558,23 +558,17 @@ bool CClient::PutClient(const CMessage& Message) { } } - // TODO: add the ability to set a list of tags to send MCString mssTags; - if (HasServerTime()) { - CString sServerTime = Msg.GetTag("time"); - if (!sServerTime.empty()) { - mssTags["time"] = sServerTime; - } else { - mssTags["time"] = CUtils::FormatServerTime(Msg.GetTime()); + for (const auto& it : Msg.GetTags()) { + if (IsTagEnabled(it.first)) { + mssTags[it.first] = it.second; } } - if (HasBatch()) { - CString sBatch = Msg.GetTag("batch"); - if (!sBatch.empty()) { - mssTags["batch"] = sBatch; - } + if (HasServerTime()) { + // If the server didn't set the time tag, manually set it + mssTags.emplace("time", CUtils::FormatServerTime(Msg.GetTime())); } Msg.SetTags(mssTags); @@ -823,6 +817,14 @@ void CClient::ParseIdentifier(const CString& sAuthLine) { } } +void CClient::SetTagSupport(const CString& sTag, bool bState) { + if (bState) { + m_ssSupportedTags.insert(sTag); + } else { + m_ssSupportedTags.erase(sTag); + } +} + void CClient::NotifyServerDependentCaps(const SCString& ssCaps) { for (const CString& sCap : ssCaps) { const auto& it = m_mCoreCaps.find(sCap); diff --git a/test/ClientTest.cpp b/test/ClientTest.cpp index 193960f8..c7b13fd9 100644 --- a/test/ClientTest.cpp +++ b/test/ClientTest.cpp @@ -188,6 +188,22 @@ TEST_F(ClientTest, StatusMsg) { m_pTestChan->GetBuffer().GetLine(0, *m_pTestClient)); } +TEST_F(ClientTest, TagSupport) { + m_pTestClient->SetTagSupport("test-tag", true); + CMessage tagmsg("@test-tag=yes;invalid-tag=no :nick!user@host PRIVMSG #chan :text"); + m_pTestClient->PutClient(tagmsg); + + EXPECT_THAT(m_pTestClient->vsLines, + ElementsAre("@test-tag=yes :nick!user@host PRIVMSG #chan :text")); + + m_pTestClient->Reset(); + m_pTestClient->SetTagSupport("test-tag", false); + m_pTestClient->PutClient(tagmsg); + + EXPECT_THAT(m_pTestClient->vsLines, + ElementsAre(":nick!user@host PRIVMSG #chan :text")); +} + TEST_F(ClientTest, OnUserCTCPReplyMessage) { CMessage msg("NOTICE someone :\001VERSION 123\001"); m_pTestModule->eAction = CModule::HALT;