diff --git a/include/znc/Client.h b/include/znc/Client.h index f048cc28..c400de91 100644 --- a/include/znc/Client.h +++ b/include/znc/Client.h @@ -227,23 +227,7 @@ class CClient : public CIRCSocket { /** Notifies client about one specific cap which server has just notified us about. */ - void NotifyServerDependentCap(const CString& sCap, bool bValue, const CString& sValue, - const std::function& handler); - /** Notifies client if the cap is server-dependent, otherwise noop. - */ - void PotentiallyNotifyServerDependentCap(const CString& sCap, bool bValue, const CString& sValue); - /** Notifies client that all these caps are now available. - * - * This function will internally filter only those which are server-dependent. - * This is when new client connects to an already connected server, and - * when server has just connected and finished negotiating caps. - */ - void NotifyServerDependentCaps(const SCString& ssCaps); - /** Notifies client that all server-dependent caps are not available anymore. - * - * Called when server disconnects. - */ - void ClearServerDependentCaps(); + void NotifyServerDependentCap(const CString& sCap, bool bValue, const CString& sValue); void ReadLine(const CString& sData) override; bool SendMotd(); @@ -323,16 +307,11 @@ class CClient : public CIRCSocket { 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. - static const std::map>>& - CoreCaps(); - // A subset of CIRCSock::GetAcceptedCaps(), the caps that can be listed - // in CAP LS and may be notified to the client with CAP NEW (cap-notify). - // TODO: come up with a way for modules to work with this, and with - // =values in NEW. - SCString m_ssServerDependentCaps; + // The capabilities supported by the ZNC core - capability names mapped to + // change handler. Note: this lists caps which don't require support on IRC + // server. + static const std::map>& + CoreCaps(); friend class ClientTest; friend class CCoreCaps; diff --git a/include/znc/IRCNetwork.h b/include/znc/IRCNetwork.h index d63296a0..a41cfd5f 100644 --- a/include/znc/IRCNetwork.h +++ b/include/znc/IRCNetwork.h @@ -156,8 +156,7 @@ class CIRCNetwork : private CCoreTranslationMixin { void IRCConnected(); void IRCDisconnected(); void CheckIRCConnect(); - void PotentiallyNotifyServerDependentCap(const CString& sCap, bool bValue); - void NotifyClientsAboutServerDependentCap(const CString& sCap, bool bValue, const std::function& handler); + void NotifyClientsAboutServerDependentCap(const CString& sCap, bool bValue); bool IsServerCapAccepted(const CString& sCap) const; bool PutIRC(const CString& sLine); diff --git a/include/znc/Modules.h b/include/znc/Modules.h index ca251d24..a250c7b2 100644 --- a/include/znc/Modules.h +++ b/include/znc/Modules.h @@ -1312,7 +1312,7 @@ class CModule { virtual void OnClientDetached(); #ifndef SWIG - void AddCapability(const CString& sName, std::unique_ptr pCap); + void AddServerDependentCapability(const CString& sName, std::unique_ptr pCap); #endif /** Called when a client told us CAP LS. Use ssCaps.insert("cap-name") @@ -1410,7 +1410,7 @@ class CModule { CString m_sArgs; CString m_sModPath; CTranslationDomainRefHolder m_Translation; - std::map> m_mCaps; + std::map> m_mServerDependentCaps; private: MCString diff --git a/modules/corecaps.cpp b/modules/corecaps.cpp index 283526e2..7b7dce59 100644 --- a/modules/corecaps.cpp +++ b/modules/corecaps.cpp @@ -22,6 +22,9 @@ #include class CCoreCaps : public CModule { + // Note: for historical reasons CClient and CIRCSock have such fields, but + // really they should not. + // TODO: move these fields and their handling from core to this module. class AwayNotify : public CCapability { void OnServerChangedSupport(CIRCNetwork* pNetwork, bool bState) override { @@ -65,10 +68,10 @@ class CCoreCaps : public CModule { public: MODCONSTRUCTOR(CCoreCaps) { - AddCapability("away-notify", std::make_unique()); - AddCapability("account-notify", std::make_unique()); - AddCapability("account-tag", std::make_unique()); - AddCapability("extended-join", std::make_unique()); + AddServerDependentCapability("away-notify", std::make_unique()); + AddServerDependentCapability("account-notify", std::make_unique()); + AddServerDependentCapability("account-tag", std::make_unique()); + AddServerDependentCapability("extended-join", std::make_unique()); } }; diff --git a/src/Client.cpp b/src/Client.cpp index e418216f..ac2dd93f 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -272,7 +272,6 @@ void CClient::SetNetwork(CIRCNetwork* pNetwork, bool bDisconnect, if (bDisconnect) { NETWORKMODULECALL(OnClientDetached(), m_pUser, m_pNetwork, this, NOTHING); - ClearServerDependentCaps(); // Tell the client they are no longer in these channels. const vector& vChans = m_pNetwork->GetChans(); for (const CChan* pChan : vChans) { @@ -743,34 +742,35 @@ static VCString MultiLine(const SCString& ssCaps) { return vsRes; } -const std::map>>& +const std::map>& CClient::CoreCaps() { - static const std::map>> mCoreCaps = []{ - std::map>> mCoreCaps = { + static const std::map> mCoreCaps = []{ + std::map> mCoreCaps = { {"multi-prefix", - {false, [](CClient* pClient, bool bVal) { pClient->m_bNamesx = bVal; }}}, + [](CClient* pClient, bool bVal) { pClient->m_bNamesx = bVal; }}, {"userhost-in-names", - {false, [](CClient* pClient, bool bVal) { pClient->m_bUHNames = bVal; }}}, + [](CClient* pClient, bool bVal) { pClient->m_bUHNames = bVal; }}, {"echo-message", - {false, [](CClient* pClient, bool bVal) { pClient->m_bEchoMessage = bVal; }}}, + [](CClient* pClient, bool bVal) { pClient->m_bEchoMessage = bVal; }}, {"server-time", - {false, [](CClient* pClient, bool bVal) { + [](CClient* pClient, bool bVal) { pClient->m_bServerTime = bVal; pClient->SetTagSupport("time", bVal); - }}}, - {"batch", {false, [](CClient* pClient, bool bVal) { + }}, + {"batch", [](CClient* pClient, bool bVal) { pClient->m_bBatch = bVal; pClient->SetTagSupport("batch", bVal); - }}}, + }}, {"cap-notify", - {false, [](CClient* pClient, bool bVal) { pClient->m_bCapNotify = bVal; }}}, + [](CClient* pClient, bool bVal) { pClient->m_bCapNotify = bVal; }}, }; // For compatibility with older clients mCoreCaps["znc.in/server-time-iso"] = mCoreCaps["server-time"]; mCoreCaps["znc.in/batch"] = mCoreCaps["batch"]; - mCoreCaps["znc.in/self-message"] = { - false, [](CClient* pClient, bool bVal) { pClient->m_bSelfMessage = bVal; }}; + mCoreCaps["znc.in/self-message"] = [](CClient* pClient, bool bVal) { + pClient->m_bSelfMessage = bVal; + }; return mCoreCaps; }(); @@ -784,10 +784,7 @@ void CClient::HandleCap(const CMessage& Message) { m_uCapVersion = std::max(m_uCapVersion, Message.GetParam(1).ToUShort()); SCString ssOfferCaps; for (const auto& it : CoreCaps()) { - bool bServerDependent = std::get<0>(it.second); - if (!bServerDependent || - m_ssServerDependentCaps.count(it.first) > 0) - ssOfferCaps.insert(it.first); + ssOfferCaps.insert(it.first); } NETWORKMODULECALL(OnClientCapLs(this, ssOfferCaps), GetUser(), GetNetwork(), this, NOTHING); VCString vsCaps = MultiLine(ssOfferCaps); @@ -823,9 +820,7 @@ void CClient::HandleCap(const CMessage& Message) { bool bAccepted = false; auto it = CoreCaps().find(sCap); if (CoreCaps().end() != it) { - bool bServerDependent = std::get<0>(it->second); - bAccepted = !bServerDependent || - m_ssServerDependentCaps.count(sCap) > 0; + bAccepted = true; } NETWORKMODULECALL(IsClientCapSupported(this, sCap, bVal), GetUser(), GetNetwork(), this, &bAccepted); @@ -845,7 +840,7 @@ void CClient::HandleCap(const CMessage& Message) { auto handler_it = CoreCaps().find(sCap); if (CoreCaps().end() != handler_it) { - const auto& handler = std::get<1>(handler_it->second); + const auto& handler = handler_it->second; handler(this, bVal); } NETWORKMODULECALL(OnClientCapRequest(this, sCap, bVal), GetUser(), GetNetwork(), this, NOTHING); @@ -927,8 +922,7 @@ void CClient::SetTagSupport(const CString& sTag, bool bState) { } } -void CClient::NotifyServerDependentCap(const CString& sCap, bool bValue, const CString& sValue, - const std::function& handler) { +void CClient::NotifyServerDependentCap(const CString& sCap, bool bValue, const CString& sValue) { if (bValue) { if (HasCapNotify()) { if (HasCap302() && !sValue.empty()) { @@ -941,90 +935,10 @@ void CClient::NotifyServerDependentCap(const CString& sCap, bool bValue, const C if (HasCapNotify()) { PutClient(":irc.znc.in CAP " + GetNick() + " DEL :" + sCap); } - handler(this, false); m_ssAcceptedCaps.erase(sCap); } } -void CClient::PotentiallyNotifyServerDependentCap(const CString& sCap, bool bValue, const CString& sValue) { - auto it = CoreCaps().find(sCap); - if (CoreCaps().end() != it) { - const auto& [bServerDependent, handler] = it->second; - if (bServerDependent) { - NotifyServerDependentCap(sCap, bValue, sValue, handler); - } - } - if (!bValue) { - m_ssServerDependentCaps.erase(sCap); - } - return; - - if (bValue) { - if (CoreCaps().end() != it) { - bool bServerDependent = std::get<0>(it->second); - if (bServerDependent) { - if (m_ssServerDependentCaps.count(sCap) == 0) { - m_ssServerDependentCaps.insert(sCap); - if (HasCapNotify()) { - PutClient(":irc.znc.in CAP " + GetNick() + " NEW :" + sCap); - } - } - } - } - } else { - if (HasCapNotify() && m_ssServerDependentCaps.count(sCap) > 0) { - PutClient(":irc.znc.in CAP " + GetNick() + " DEL :" + sCap); - } - if (CoreCaps().end() != it) { - bool bServerDependent = std::get<0>(it->second); - const auto& handler = std::get<1>(it->second); - if (bServerDependent) { - handler(this, false); - } - } - m_ssServerDependentCaps.erase(sCap); - } -} - -void CClient::NotifyServerDependentCaps(const SCString& ssCaps) { - for (const CString& sCap : ssCaps) { - auto it = CoreCaps().find(sCap); - if (CoreCaps().end() != it) { - bool bServerDependent = std::get<0>(it->second); - if (bServerDependent) { - m_ssServerDependentCaps.insert(sCap); - } - } - } - - if (HasCapNotify() && !m_ssServerDependentCaps.empty()) { - VCString vsCaps = MultiLine(m_ssServerDependentCaps); - for (const CString& sLine : vsCaps) { - PutClient(":irc.znc.in CAP " + GetNick() + " NEW :" + sLine); - } - } -} - -void CClient::ClearServerDependentCaps() { - if (HasCapNotify() && !m_ssServerDependentCaps.empty()) { - VCString vsCaps = MultiLine(m_ssServerDependentCaps); - for (const CString& sLine : vsCaps) { - PutClient(":irc.znc.in CAP " + GetNick() + " DEL :" + sLine); - } - - for (const CString& sCap : m_ssServerDependentCaps) { - auto it = CoreCaps().find(sCap); - if (CoreCaps().end() != it) { - const auto& handler = std::get<1>(it->second); - handler(this, false); - } - m_ssAcceptedCaps.erase(sCap); - } - } - - m_ssServerDependentCaps.clear(); -} - template void CClient::AddBuffer(const T& Message) { if (!m_pNetwork) { diff --git a/src/IRCNetwork.cpp b/src/IRCNetwork.cpp index 30e0149b..e46df7d0 100644 --- a/src/IRCNetwork.cpp +++ b/src/IRCNetwork.cpp @@ -660,10 +660,6 @@ void CIRCNetwork::ClientConnected(CClient* pClient) { size_t uIdx, uSize; - if (m_pIRCSock) { - pClient->NotifyServerDependentCaps(m_pIRCSock->GetAcceptedCaps()); - } - pClient->SetPlaybackActive(true); if (m_RawBuffer.IsEmpty()) { @@ -1393,10 +1389,6 @@ bool CIRCNetwork::IsIRCConnected() const { void CIRCNetwork::SetIRCSocket(CIRCSock* pIRCSock) { m_pIRCSock = pIRCSock; } void CIRCNetwork::IRCConnected() { - const SCString& ssCaps = m_pIRCSock->GetAcceptedCaps(); - for (CClient* pClient : m_vClients) { - pClient->NotifyServerDependentCaps(ssCaps); - } if (m_uJoinDelay > 0) { m_pJoinTimer->Delay(m_uJoinDelay); } else { @@ -1405,9 +1397,6 @@ void CIRCNetwork::IRCConnected() { } void CIRCNetwork::IRCDisconnected() { - for (CClient* pClient : m_vClients) { - pClient->ClearServerDependentCaps(); - } m_pIRCSock = nullptr; SetIRCServer(""); @@ -1417,17 +1406,10 @@ void CIRCNetwork::IRCDisconnected() { CheckIRCConnect(); } -void CIRCNetwork::PotentiallyNotifyServerDependentCap(const CString& sCap, bool bValue) { +void CIRCNetwork::NotifyClientsAboutServerDependentCap(const CString& sCap, bool bValue) { CString sValue = GetIRCSock() ? GetIRCSock()->GetCapLsValue(sCap) : ""; for (CClient* pClient : m_vClients) { - pClient->PotentiallyNotifyServerDependentCap(sCap, bValue, sValue); - } -} - -void CIRCNetwork::NotifyClientsAboutServerDependentCap(const CString& sCap, bool bValue, const std::function& handler) { - CString sValue = GetIRCSock() ? GetIRCSock()->GetCapLsValue(sCap) : ""; - for (CClient* pClient : m_vClients) { - pClient->NotifyServerDependentCap(sCap, bValue, sValue, handler); + pClient->NotifyServerDependentCap(sCap, bValue, sValue); } } diff --git a/src/IRCSock.cpp b/src/IRCSock.cpp index 28973e12..e3473707 100644 --- a/src/IRCSock.cpp +++ b/src/IRCSock.cpp @@ -393,9 +393,6 @@ bool CIRCSock::OnCapabilityMessage(CMessage& Message) { } m_ssAcceptedCaps.erase(sCap); m_ssPendingCaps.erase(sCap); - if (m_bAuthed) { - m_pNetwork->PotentiallyNotifyServerDependentCap(sCap, false); - } }; if (sSubCmd == "LS" || sSubCmd == "NEW") { @@ -424,9 +421,6 @@ bool CIRCSock::OnCapabilityMessage(CMessage& Message) { it->second(true); } m_ssAcceptedCaps.insert(sArgs); - if (m_bAuthed) { - m_pNetwork->PotentiallyNotifyServerDependentCap(sArgs, true); - } } else if (sSubCmd == "NAK") { // This should work because there's no [known] // capability with length of name more than 100 characters. diff --git a/src/Modules.cpp b/src/Modules.cpp index 85c033d8..8d61aeec 100644 --- a/src/Modules.cpp +++ b/src/Modules.cpp @@ -161,19 +161,19 @@ CModule::CModule(ModHandle pDLL, CUser* pUser, CIRCNetwork* pNetwork, } CModule::~CModule() { - for (const auto& [sName, pCap] : m_mCaps) { + for (const auto& [sName, pCap] : m_mServerDependentCaps) { switch (GetType()) { case CModInfo::NetworkModule: - GetNetwork()->NotifyClientsAboutServerDependentCap( - sName, false, [&](CClient* pClient, bool bState) {}); + GetNetwork()->NotifyClientsAboutServerDependentCap(sName, + false); for (CClient* pClient : GetNetwork()->GetClients()) { pCap->OnClientChangedSupport(pClient, false); } break; case CModInfo::UserModule: for (CIRCNetwork* pNetwork : GetUser()->GetNetworks()) { - pNetwork->NotifyClientsAboutServerDependentCap( - sName, false, [&](CClient* pClient, bool bState) {}); + pNetwork->NotifyClientsAboutServerDependentCap(sName, + false); for (CClient* pClient : pNetwork->GetClients()) { pCap->OnClientChangedSupport(pClient, false); } @@ -182,8 +182,8 @@ CModule::~CModule() { case CModInfo::GlobalModule: for (auto& [_, pUser] : CZNC::Get().GetUserMap()) { for (CIRCNetwork* pNetwork : pUser->GetNetworks()) { - pNetwork->NotifyClientsAboutServerDependentCap( - sName, false, [&](CClient* pClient, bool bState) {}); + pNetwork->NotifyClientsAboutServerDependentCap(sName, + false); for (CClient* pClient : pNetwork->GetClients()) { pCap->OnClientChangedSupport(pClient, false); } @@ -638,18 +638,17 @@ bool CModule::OnBoot() { return true; } void CModule::OnPreRehash() {} void CModule::OnPostRehash() {} void CModule::OnIRCDisconnected() { - for (const auto& [sName, pCap] : m_mCaps) { - GetNetwork()->NotifyClientsAboutServerDependentCap( - sName, false, [&](CClient* pClient, bool bState) {}); + for (const auto& [sName, pCap] : m_mServerDependentCaps) { + GetNetwork()->NotifyClientsAboutServerDependentCap(sName, false); for (CClient* pClient : GetNetwork()->GetClients()) { pCap->OnClientChangedSupport(pClient, false); } } } void CModule::OnIRCConnected() { - for (const auto& [sName, pCap] : m_mCaps) { + for (const auto& [sName, pCap] : m_mServerDependentCaps) { if (GetNetwork()->IsServerCapAccepted(sName)) { - GetNetwork()->NotifyClientsAboutServerDependentCap(sName, true, [](CClient* pClient, bool bState){}); + GetNetwork()->NotifyClientsAboutServerDependentCap(sName, true); } } } @@ -1045,16 +1044,15 @@ CModule::EModRet CModule::OnSendToIRCMessage(CMessage& Message) { } void CModule::OnClientAttached() { if (!GetNetwork()) return; - for (const auto& [sName, pCap] : m_mCaps) { + for (const auto& [sName, pCap] : m_mServerDependentCaps) { if (GetNetwork()->IsServerCapAccepted(sName)) { - GetClient()->NotifyServerDependentCap(sName, true, GetNetwork()->GetIRCSock()->GetCapLsValue(sName), nullptr); + GetClient()->NotifyServerDependentCap(sName, true, GetNetwork()->GetIRCSock()->GetCapLsValue(sName)); } } } void CModule::OnClientDetached() { - for (const auto& [sName, pCap] : m_mCaps) { - GetClient()->NotifyServerDependentCap(sName, false, "", - [](CClient*, bool) {}); + for (const auto& [sName, pCap] : m_mServerDependentCaps) { + GetClient()->NotifyServerDependentCap(sName, false, ""); pCap->OnClientChangedSupport(GetClient(), false); } } @@ -1062,23 +1060,22 @@ void CModule::OnClientDetached() { bool CModule::OnServerCapAvailable(const CString& sCap) { return false; } bool CModule::OnServerCap302Available(const CString& sCap, const CString& sValue) { - auto it = m_mCaps.find(sCap); - if (it == m_mCaps.end()) return OnServerCapAvailable(sCap); + auto it = m_mServerDependentCaps.find(sCap); + if (it == m_mServerDependentCaps.end()) return OnServerCapAvailable(sCap); if (GetNetwork()->IsServerCapAccepted(sCap)) { // This can happen when server sent CAP NEW with another value. - GetNetwork()->NotifyClientsAboutServerDependentCap(sCap, true, nullptr); + GetNetwork()->NotifyClientsAboutServerDependentCap(sCap, true); // It's enabled already, no need to REQ it again. return false; } return true; } void CModule::OnServerCapResult(const CString& sCap, bool bSuccess) { - auto it = m_mCaps.find(sCap); - if (it == m_mCaps.end()) return; + auto it = m_mServerDependentCaps.find(sCap); + if (it == m_mServerDependentCaps.end()) return; it->second->OnServerChangedSupport(GetNetwork(), bSuccess); if (GetNetwork()->GetIRCSock()->IsAuthed()) { - GetNetwork()->NotifyClientsAboutServerDependentCap( - sCap, bSuccess, [&](CClient* pClient, bool bState) {}); + GetNetwork()->NotifyClientsAboutServerDependentCap(sCap, bSuccess); if (!bSuccess) { for (CClient* pClient : GetNetwork()->GetClients()) { it->second->OnClientChangedSupport(pClient, false); @@ -1155,7 +1152,7 @@ CModule::EModRet CModule::OnUnknownUserRawMessage(CMessage& Message) { return CONTINUE; } void CModule::OnClientCapLs(CClient* pClient, SCString& ssCaps) { - for (const auto& [sName, pCap] : m_mCaps) { + for (const auto& [sName, pCap] : m_mServerDependentCaps) { if (GetNetwork() && GetNetwork()->IsServerCapAccepted(sName)) { if (pClient->HasCap302()) { CString sValue = @@ -1173,15 +1170,15 @@ void CModule::OnClientCapLs(CClient* pClient, SCString& ssCaps) { } bool CModule::IsClientCapSupported(CClient* pClient, const CString& sCap, bool bState) { - auto it = m_mCaps.find(sCap); - if (it == m_mCaps.end()) return false; + auto it = m_mServerDependentCaps.find(sCap); + if (it == m_mServerDependentCaps.end()) return false; if (!bState) return true; return GetNetwork() && GetNetwork()->IsServerCapAccepted(sCap); } void CModule::OnClientCapRequest(CClient* pClient, const CString& sCap, bool bState) { - auto it = m_mCaps.find(sCap); - if (it == m_mCaps.end()) return; + auto it = m_mServerDependentCaps.find(sCap); + if (it == m_mServerDependentCaps.end()) return; it->second->OnClientChangedSupport(pClient, bState); } CModule::EModRet CModule::OnModuleLoading(const CString& sModName, @@ -1201,10 +1198,10 @@ CModule::EModRet CModule::OnGetModInfo(CModInfo& ModInfo, } void CModule::OnGetAvailableMods(set& ssMods, CModInfo::EModuleType eType) {} -void CModule::AddCapability(const CString& sName, - std::unique_ptr pCap) { +void CModule::AddServerDependentCapability(const CString& sName, + std::unique_ptr pCap) { pCap->SetModule(this); - m_mCaps[sName] = std::move(pCap); + m_mServerDependentCaps[sName] = std::move(pCap); } CModules::CModules() diff --git a/test/integration/tests/core.cpp b/test/integration/tests/core.cpp index 8e41494d..b82abda0 100644 --- a/test/integration/tests/core.cpp +++ b/test/integration/tests/core.cpp @@ -553,7 +553,7 @@ TEST_F(ZNCTest, ServerDependentCapInModule) { }; public: MODCONSTRUCTOR(TestModule) { - AddCapability("testcap", std::make_unique()); + AddServerDependentCapability("testcap", std::make_unique()); } }; MODULEDEFS(TestModule, "Test")