diff --git a/include/znc/Modules.h b/include/znc/Modules.h index c7dde8ee..f963f4c7 100644 --- a/include/znc/Modules.h +++ b/include/znc/Modules.h @@ -720,6 +720,11 @@ public: * @return See CModule::EModRet. */ virtual EModRet OnUserTopicRequest(CString& sChannel); + /** This module hook is called when a user requests to quit from network. + * @param sMessage The quit message the client sent. + * @return See CModule::EModRet. + */ + virtual EModRet OnUserQuit(CString& sMessage); /** Called when we receive a CTCP reply from IRC. * @param Nick The nick the CTCP reply is from. @@ -1190,6 +1195,7 @@ public: bool OnUserPart(CString& sChannel, CString& sMessage); bool OnUserTopic(CString& sChannel, CString& sTopic); bool OnUserTopicRequest(CString& sChannel); + bool OnUserQuit(CString& sMessage); bool OnCTCPReply(CNick& Nick, CString& sMessage); bool OnPrivCTCP(CNick& Nick, CString& sMessage); diff --git a/modules/clearbufferonmsg.cpp b/modules/clearbufferonmsg.cpp index 37a5305b..557e7680 100644 --- a/modules/clearbufferonmsg.cpp +++ b/modules/clearbufferonmsg.cpp @@ -15,72 +15,154 @@ */ #include +#include #include #include using std::vector; +enum { + RULE_MSG, + RULE_CTCP, + RULE_ACTION, + RULE_NOTICE, + RULE_PART, + RULE_TOPIC, + RULE_QUIT, + RULE_MAX, +}; + class CClearBufferOnMsgMod : public CModule { public: - MODCONSTRUCTOR(CClearBufferOnMsgMod) {} + MODCONSTRUCTOR(CClearBufferOnMsgMod) { + SetAllRules(true); + // false for backward compatibility + m_bRules[RULE_QUIT] = false; + } void ClearAllBuffers() { CIRCNetwork* pNetwork = GetNetwork(); + if (pNetwork) { const vector& vChans = pNetwork->GetChans(); - for (vector::const_iterator it = vChans.begin(); it != vChans.end(); ++it) { + for (CChan* pChan : vChans) { // Skip detached channels, they weren't read yet - if ((*it)->IsDetached()) + if (pChan->IsDetached()) continue; - (*it)->ClearBuffer(); + pChan->ClearBuffer(); // We deny AutoClearChanBuffer on all channels since this module // doesn't make any sense with it - (*it)->SetAutoClearChanBuffer(false); + pChan->SetAutoClearChanBuffer(false); } vector VQueries = pNetwork->GetQueries(); - for (vector::const_iterator it = VQueries.begin(); it != VQueries.end(); ++it) { - pNetwork->DelQuery((*it)->GetName()); + for (CQuery* pQuery : VQueries) { + pNetwork->DelQuery(pQuery->GetName()); } + + // We deny AutoClearQueryBuffer since this module + // doesn't make any sense with it + GetUser()->SetAutoClearQueryBuffer(false); } } virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage) override { - ClearAllBuffers(); + if (m_bRules[RULE_MSG]) + ClearAllBuffers(); return CONTINUE; } virtual EModRet OnUserCTCP(CString& sTarget, CString& sMessage) override { - ClearAllBuffers(); + if (m_bRules[RULE_CTCP]) + ClearAllBuffers(); return CONTINUE; } virtual EModRet OnUserAction(CString& sTarget, CString& sMessage) override { - ClearAllBuffers(); + if (m_bRules[RULE_ACTION]) + ClearAllBuffers(); return CONTINUE; } virtual EModRet OnUserNotice(CString& sTarget, CString& sMessage) override { - ClearAllBuffers(); + if (m_bRules[RULE_NOTICE]) + ClearAllBuffers(); return CONTINUE; } virtual EModRet OnUserPart(CString& sChannel, CString& sMessage) override { - ClearAllBuffers(); + if (m_bRules[RULE_PART]) + ClearAllBuffers(); return CONTINUE; } virtual EModRet OnUserTopic(CString& sChannel, CString& sTopic) override { - ClearAllBuffers(); + if (m_bRules[RULE_TOPIC]) + ClearAllBuffers(); return CONTINUE; } + + virtual EModRet OnUserQuit(CString& sMessage) override { + if (m_bRules[RULE_QUIT]) + ClearAllBuffers(); + return CONTINUE; + } + + void SetAllRules(bool bVal) { + for (int i = 0; i < RULE_MAX; i++) + m_bRules[i] = bVal; + } + + void SetRule(const CString& sOpt, bool bVal) { + static const struct { + CString sName; + int Index; + } Names[RULE_MAX] = { + { "msg", RULE_MSG }, + { "ctcp", RULE_CTCP }, + { "action", RULE_ACTION }, + { "notice", RULE_NOTICE }, + { "part", RULE_PART }, + { "topic", RULE_TOPIC }, + { "quit", RULE_QUIT }, + }; + + if (sOpt.Equals("all")) { + SetAllRules(bVal); + } else { + for (int i = 0; i < RULE_MAX; i++) { + if (sOpt.Equals(Names[i].sName)) + m_bRules[Names[i].Index] = bVal; + } + } + } + + virtual bool OnLoad(const CString& sArgs, CString& sMessage) override { + VCString vsOpts; + + sArgs.Split(" ", vsOpts, false); + + for (CString& sOpt : vsOpts) { + if (sOpt.StartsWith("!")) + SetRule(sOpt.substr(1), false); + else if (!sOpt.empty()) + SetRule(sOpt, true); + } + + return true; + } + +private: + bool m_bRules[RULE_MAX]; }; template<> void TModInfo(CModInfo& Info) { Info.SetWikiPage("clearbufferonmsg"); + Info.SetHasArgs(true); + Info.SetArgsHelpText("[ [!] ]"); } USERMODULEDEFS(CClearBufferOnMsgMod, "Clear all channel and query buffers whenever the user does something") diff --git a/modules/modperl/functions.in b/modules/modperl/functions.in index 64ed0b41..e3f58298 100644 --- a/modules/modperl/functions.in +++ b/modules/modperl/functions.in @@ -47,6 +47,7 @@ EModRet OnUserJoin(CString& sChannel, CString& sKey) EModRet OnUserPart(CString& sChannel, CString& sMessage) EModRet OnUserTopic(CString& sChannel, CString& sTopic) EModRet OnUserTopicRequest(CString& sChannel) +EModRet OnUserQuit(CString& sMessage) EModRet OnCTCPReply(CNick& Nick, CString& sMessage) EModRet OnPrivCTCP(CNick& Nick, CString& sMessage) EModRet OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage) diff --git a/modules/modperl/module.h b/modules/modperl/module.h index 28574708..acab1885 100644 --- a/modules/modperl/module.h +++ b/modules/modperl/module.h @@ -86,6 +86,7 @@ public: virtual EModRet OnUserJoin(CString& sChannel, CString& sKey) override; virtual EModRet OnUserPart(CString& sChannel, CString& sMessage) override; virtual EModRet OnUserTopic(CString& sChannel, CString& sTopic) override; + virtual EModRet OnUserQuit(CString& sMessage) override; virtual EModRet OnUserTopicRequest(CString& sChannel) override; virtual EModRet OnCTCPReply(CNick& Nick, CString& sMessage) override; virtual EModRet OnPrivCTCP(CNick& Nick, CString& sMessage) override; diff --git a/modules/modperl/startup.pl b/modules/modperl/startup.pl index f47a465d..55d095ea 100644 --- a/modules/modperl/startup.pl +++ b/modules/modperl/startup.pl @@ -356,6 +356,7 @@ sub OnUserJoin {} sub OnUserPart {} sub OnUserTopic {} sub OnUserTopicRequest {} +sub OnUserQuit {} sub OnCTCPReply {} sub OnPrivCTCP {} sub OnChanCTCP {} diff --git a/modules/modpython/functions.in b/modules/modpython/functions.in index 5bf893d2..5e324687 100644 --- a/modules/modpython/functions.in +++ b/modules/modpython/functions.in @@ -47,6 +47,7 @@ EModRet OnUserJoin(CString& sChannel, CString& sKey) EModRet OnUserPart(CString& sChannel, CString& sMessage) EModRet OnUserTopic(CString& sChannel, CString& sTopic) EModRet OnUserTopicRequest(CString& sChannel) +EModRet OnUserQuit(CString& sMessage) EModRet OnCTCPReply(CNick& Nick, CString& sMessage) EModRet OnPrivCTCP(CNick& Nick, CString& sMessage) EModRet OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage) diff --git a/modules/modpython/module.h b/modules/modpython/module.h index 97ed8230..56be98f4 100644 --- a/modules/modpython/module.h +++ b/modules/modpython/module.h @@ -103,6 +103,7 @@ public: virtual EModRet OnUserPart(CString& sChannel, CString& sMessage) override; virtual EModRet OnUserTopic(CString& sChannel, CString& sTopic) override; virtual EModRet OnUserTopicRequest(CString& sChannel) override; + virtual EModRet OnUserQuit(CString& sMessage) override; virtual EModRet OnCTCPReply(CNick& Nick, CString& sMessage) override; virtual EModRet OnPrivCTCP(CNick& Nick, CString& sMessage) override; virtual EModRet OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage) override; diff --git a/modules/modpython/znc.py b/modules/modpython/znc.py index 81650f10..cd4de4b3 100644 --- a/modules/modpython/znc.py +++ b/modules/modpython/znc.py @@ -339,6 +339,9 @@ class Module: def OnUserTopicRequest(self, sChannel): pass + def OnUserQuit(self, sMessage): + pass + def OnCTCPReply(self, Nick, sMessage): pass diff --git a/src/Client.cpp b/src/Client.cpp index fce77421..b9377daa 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -198,6 +198,9 @@ void CClient::ReadLine(const CString& sData) { // Block PONGs, we already responded to the pings return; } else if (sCommand.Equals("QUIT")) { + CString sMsg = sLine.Token(1, true).TrimPrefix_n(); + NETWORKMODULECALL(OnUserQuit(sMsg), m_pUser, m_pNetwork, this, &bReturn); + if (bReturn) return; Close(Csock::CLT_AFTERWRITE); // Treat a client quit as a detach return; // Don't forward this msg. We don't want the client getting us disconnected. } else if (sCommand.Equals("PROTOCTL")) { diff --git a/src/Modules.cpp b/src/Modules.cpp index f7e9992b..55a1a311 100644 --- a/src/Modules.cpp +++ b/src/Modules.cpp @@ -693,6 +693,7 @@ CModule::EModRet CModule::OnUserJoin(CString& sChannel, CString& sKey) { return CModule::EModRet CModule::OnUserPart(CString& sChannel, CString& sMessage) { return CONTINUE; } CModule::EModRet CModule::OnUserTopic(CString& sChannel, CString& sTopic) { return CONTINUE; } CModule::EModRet CModule::OnUserTopicRequest(CString& sChannel) { return CONTINUE; } +CModule::EModRet CModule::OnUserQuit(CString& sMessage) { return CONTINUE; } CModule::EModRet CModule::OnCTCPReply(CNick& Nick, CString& sMessage) { return CONTINUE; } CModule::EModRet CModule::OnPrivCTCP(CNick& Nick, CString& sMessage) { return CONTINUE; } @@ -854,6 +855,7 @@ bool CModules::OnUserJoin(CString& sChannel, CString& sKey) { MODHALTCHK(OnUserJ bool CModules::OnUserPart(CString& sChannel, CString& sMessage) { MODHALTCHK(OnUserPart(sChannel, sMessage)); } bool CModules::OnUserTopic(CString& sChannel, CString& sTopic) { MODHALTCHK(OnUserTopic(sChannel, sTopic)); } bool CModules::OnUserTopicRequest(CString& sChannel) { MODHALTCHK(OnUserTopicRequest(sChannel)); } +bool CModules::OnUserQuit(CString& sMessage) { MODHALTCHK(OnUserQuit(sMessage)); } bool CModules::OnQuit(const CNick& Nick, const CString& sMessage, const vector& vChans) { MODUNLOADCHK(OnQuit(Nick, sMessage, vChans)); return false; } bool CModules::OnNick(const CNick& Nick, const CString& sNewNick, const vector& vChans) { MODUNLOADCHK(OnNick(Nick, sNewNick, vChans)); return false; }