diff --git a/Client.cpp b/Client.cpp index a25a2616..f8174dfc 100644 --- a/Client.cpp +++ b/Client.cpp @@ -783,33 +783,29 @@ void CClient::HandleCap(const CString& sLine) CString sSubCmd = sLine.Token(1); if (sSubCmd.Equals("LS")) { - RespondCap("LS :userhost-in-names multi-prefix"); + SCString ssOfferCaps; + CZNC::Get().GetModules().OnClientCapLs(ssOfferCaps); + CString sRes; + for (SCString::iterator i = ssOfferCaps.begin(); i != ssOfferCaps.end(); ++i) { + sRes += *i + " "; + } + RespondCap("LS :" + sRes + "userhost-in-names multi-prefix"); m_bInCap = true; } else if (sSubCmd.Equals("END")) { m_bInCap = false; AuthUser(); } else if (sSubCmd.Equals("REQ")) { - bool bReqUHNames = false; - bool bReqNamesx = false; - bool bValueUHNames = false; - bool bValueNamesx = false; - VCString vsTokens; VCString::iterator it; sLine.Token(2, true).TrimPrefix_n(":").Split(" ", vsTokens, false); for (it = vsTokens.begin(); it != vsTokens.end(); ++it) { bool bVal = true; - if (it->TrimPrefix("-")) + CString sCap = *it; + if (sCap.TrimPrefix("-")) bVal = false; - if (*it == "multi-prefix") { - bReqNamesx = true; - bValueNamesx = bVal; - } else if (*it == "userhost-in-names") { - bReqUHNames = true; - bValueUHNames = bVal; - } else { + if ("multi-prefix" != sCap && "userhost-in-names" != sCap && !CZNC::Get().GetModules().IsClientCapSupported(sCap, bVal)) { // Some unsupported capability is requested RespondCap("NAK :" + sLine.Token(2, true).TrimPrefix_n(":")); return; @@ -817,17 +813,32 @@ void CClient::HandleCap(const CString& sLine) } // All is fine, we support what was requested + for (it = vsTokens.begin(); it != vsTokens.end(); ++it) { + bool bVal = true; + if (it->TrimPrefix("-")) + bVal = false; + + if ("multi-prefix" == *it) { + m_bNamesx = bVal; + } else if ("userhost-in-names" == *it) { + m_bUHNames = bVal; + } else { + CZNC::Get().GetModules().OnClientCapRequest(this, *it, bVal); + } + + if (bVal) { + m_ssAcceptedCaps.insert(*it); + } else { + m_ssAcceptedCaps.erase(*it); + } + } + RespondCap("ACK :" + sLine.Token(2, true).TrimPrefix_n(":")); - if (bReqUHNames) - m_bUHNames = bValueUHNames; - if (bReqNamesx) - m_bNamesx = bValueNamesx; } else if (sSubCmd.Equals("LIST")) { CString sList = ""; - if (m_bNamesx) - sList += "multi-prefix "; - if (m_bUHNames) - sList += "userhost-in-names "; + for (SCString::iterator i = m_ssAcceptedCaps.begin(); i != m_ssAcceptedCaps.end(); ++i) { + sList += *i + " "; + } RespondCap("LIST :" + sList.TrimSuffix_n(" ")); } } diff --git a/Client.h b/Client.h index aafe6ce4..c147776b 100644 --- a/Client.h +++ b/Client.h @@ -113,6 +113,8 @@ public: void PutModule(const CString& sModule, const CString& sLine); void PutModNotice(const CString& sModule, const CString& sLine); + bool IsCapEnabled(const CString& sCap) { return 1 == m_ssAcceptedCaps.count(sCap); } + virtual void ReadLine(const CString& sData); bool SendMotd(); void HelpUser(); @@ -143,6 +145,7 @@ protected: CString m_sPass; CString m_sUser; CSmartPtr m_spAuth; + SCString m_ssAcceptedCaps; }; #endif // !_CLIENT_H diff --git a/Modules.cpp b/Modules.cpp index 1a98f962..e10277c6 100644 --- a/Modules.cpp +++ b/Modules.cpp @@ -503,6 +503,9 @@ void CGlobalModule::OnClientConnect(CZNCSock* pClient, const CString& sHost, uns CModule::EModRet CGlobalModule::OnLoginAttempt(CSmartPtr Auth) { return CONTINUE; } void CGlobalModule::OnFailedLogin(const CString& sUsername, const CString& sRemoteIP) {} CModule::EModRet CGlobalModule::OnUnknownUserRaw(CClient* pClient, CString& sLine) { return CONTINUE; } +void CGlobalModule::OnClientCapLs(SCString& ssCaps) {} +bool CGlobalModule::IsClientCapSupported(const CString& sCap, bool bState) { return false; } +void CGlobalModule::OnClientCapRequest(CClient* pClient, const CString& sCap, bool bState) {} CModules::CModules() { @@ -627,6 +630,42 @@ bool CGlobalModules::OnUnknownUserRaw(CClient* pClient, CString& sLine) { GLOBALMODHALTCHK(OnUnknownUserRaw(pClient, sLine)); } +void CGlobalModules::OnClientCapLs(SCString& ssCaps) { + GLOBALMODCALL(OnClientCapLs(ssCaps)); +} + +// Maybe create new macro for this? +bool CGlobalModules::IsClientCapSupported(const CString& sCap, bool bState) { + bool bResult = false; + for (unsigned int a = 0; a < size(); ++a) { + try { + CGlobalModule* pMod = (CGlobalModule*) (*this)[a]; + CClient* pOldClient = pMod->GetClient(); + pMod->SetClient(m_pClient); + if (m_pUser) { + CUser* pOldUser = pMod->GetUser(); + pMod->SetUser(m_pUser); + bResult |= pMod->IsClientCapSupported(sCap, bState); + pMod->SetUser(pOldUser); + } else { + // WTF? Is that possible? + bResult |= pMod->IsClientCapSupported(sCap, bState); + } + pMod->SetClient(pOldClient); + } catch (CModule::EModException e) { + if (CModule::UNLOAD == e) { + UnloadModule((*this)[a]->GetModName()); + } + } + } + return bResult; +} + +void CGlobalModules::OnClientCapRequest(CClient* pClient, const CString& sCap, bool bState) { + GLOBALMODCALL(OnClientCapRequest(pClient, sCap, bState)); +} + + CModule* CModules::FindModule(const CString& sModule) const { for (unsigned int a = 0; a < size(); a++) { if (sModule.Equals((*this)[a]->GetModName())) { diff --git a/Modules.h b/Modules.h index 6a38b58c..75b4a159 100644 --- a/Modules.h +++ b/Modules.h @@ -959,6 +959,24 @@ public: * (Well, ok, m_pUser isn't known yet...) */ virtual EModRet OnUnknownUserRaw(CClient* pClient, CString& sLine); + + /** Called when a client told us CAP LS. Use ssCaps.insert("cap-name") + * for announcing capabilities which your module supports. + * @param pClient client which wants caps. + */ + virtual void OnClientCapLs(SCString& ssCaps); + /** Called only to check if your module supports turning on/off named capability. + * @param sCap name of capability. + * @param bState On or off, depending on which case is interesting for client. + * @return true if your module supports this capability in the specified state. + */ + virtual bool IsClientCapSupported(const CString& sCap, bool bState); + /** Called when we actually need to turn a capability on or off for a client. + * @param pClient client which requested this. + * @param sCap name of wanted capability. + * @param bState On or off, depending on which case client needs. + */ + virtual void OnClientCapRequest(CClient* pClient, const CString& sCap, bool bState); private: }; @@ -974,6 +992,9 @@ public: bool OnLoginAttempt(CSmartPtr Auth); void OnFailedLogin(const CString& sUsername, const CString& sRemoteIP); bool OnUnknownUserRaw(CClient* pClient, CString& sLine); + void OnClientCapLs(SCString& ssCaps); + bool IsClientCapSupported(const CString& sCap, bool bState); + void OnClientCapRequest(CClient* pClient, const CString& sCap, bool bState); private: };