From b666931883ee3c8c0dcd4c62065410b2e8608590 Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Mon, 26 Dec 2016 15:49:26 +0000 Subject: [PATCH] Add an option to change ping timeout time. Fix #979 --- include/znc/IRCNetwork.h | 19 +++++-------------- include/znc/User.h | 10 ++++++++++ modules/controlpanel.cpp | 12 ++++++++++++ modules/data/webadmin/tmpl/add_edit_user.tmpl | 5 +++++ modules/webadmin.cpp | 10 ++++++++++ src/Client.cpp | 2 +- src/IRCNetwork.cpp | 19 ++++++++++++------- src/IRCSock.cpp | 2 +- src/User.cpp | 14 +++++++++++--- 9 files changed, 67 insertions(+), 26 deletions(-) diff --git a/include/znc/IRCNetwork.h b/include/znc/IRCNetwork.h index 9bf1304c..99a1f124 100644 --- a/include/znc/IRCNetwork.h +++ b/include/znc/IRCNetwork.h @@ -48,18 +48,6 @@ class CIRCNetwork { CIRCNetwork(const CIRCNetwork&) = delete; CIRCNetwork& operator=(const CIRCNetwork&) = delete; - enum { - JOIN_FREQUENCY = 30, - /** How long must an IRC connection be idle before ZNC sends a ping */ - PING_FREQUENCY = 120, - /** Time between checks if PINGs need to be sent */ - PING_SLACK = 30, - /** Timeout after which IRC connections are closed. Must - * obviously be greater than PING_FREQUENCY + PING_SLACK. - */ - NO_TRAFFIC_TIMEOUT = 180 - }; - void Clone(const CIRCNetwork& Network, bool bCloneName = true); CString GetNetworkPath() const; @@ -157,7 +145,8 @@ class CIRCNetwork { void SetIRCAway(bool b) { m_bIRCAway = b; } bool Connect(); - /** This method will return whether the user is connected and authenticated to an IRC server. + /** This method will return whether the user is connected and authenticated + * to an IRC server. */ bool IsIRCConnected() const; void SetIRCSocket(CIRCSock* pIRCSock); @@ -266,7 +255,9 @@ class CIRCNetwork { m_uJoinDelay = uJoinDelay; } - void SetTrustAllCerts(const bool bTrustAll = false) { m_bTrustAllCerts = bTrustAll; } + void SetTrustAllCerts(const bool bTrustAll = false) { + m_bTrustAllCerts = bTrustAll; + } bool GetTrustAllCerts() const { return m_bTrustAllCerts; } void SetTrustPKI(const bool bTrustPKI = true) { m_bTrustPKI = bTrustPKI; } diff --git a/include/znc/User.h b/include/znc/User.h index 74c248b8..7673d960 100644 --- a/include/znc/User.h +++ b/include/znc/User.h @@ -155,6 +155,7 @@ class CUser { void SetSkinName(const CString& s) { m_sSkinName = s; } void SetMaxNetworks(unsigned int i) { m_uMaxNetworks = i; } void SetMaxQueryBuffers(unsigned int i) { m_uMaxQueryBuffers = i; } + void SetNoTrafficTimeout(unsigned int i) { m_uNoTrafficTimeout = i; } // !Setters // Getters @@ -185,6 +186,14 @@ class CUser { bool MultiClients() const; const CString& GetStatusPrefix() const; const CString& GetDefaultChanModes() const; + /** How long must an IRC connection be idle before ZNC sends a ping */ + unsigned int GetPingFrequency() const { return m_uNoTrafficTimeout / 2; } + /** Time between checks if PINGs need to be sent */ + unsigned int GetPingSlack() const { return m_uNoTrafficTimeout / 6; } + /** Timeout after which IRC connections are closed. Must + * obviously be greater than GetPingFrequency() + GetPingSlack(). + */ + unsigned int GetNoTrafficTimeout() const { return m_uNoTrafficTimeout; } CString GetQuitMsg() const; const MCString& GetCTCPReplies() const; @@ -254,6 +263,7 @@ class CUser { unsigned int m_uMaxNetworks; unsigned int m_uMaxQueryBuffers; unsigned int m_uMaxJoins; + unsigned int m_uNoTrafficTimeout; CString m_sSkinName; CString m_sLanguage; diff --git a/modules/controlpanel.cpp b/modules/controlpanel.cpp index 95c0bae0..e1ca243e 100644 --- a/modules/controlpanel.cpp +++ b/modules/controlpanel.cpp @@ -249,6 +249,9 @@ class CAdminMod : public CModule { CString(pUser->AutoClearQueryBuffer())); else if (sVar == "maxjoins") PutModule("MaxJoins = " + CString(pUser->MaxJoins())); + else if (sVar == "notraffictimeout") + PutModule("NoTrafficTimeout = " + + CString(pUser->GetNoTrafficTimeout())); else if (sVar == "maxnetworks") PutModule("MaxNetworks = " + CString(pUser->MaxNetworks())); else if (sVar == "maxquerybuffers") @@ -386,6 +389,15 @@ class CAdminMod : public CModule { unsigned int i = sValue.ToUInt(); pUser->SetMaxJoins(i); PutModule("MaxJoins = " + CString(pUser->MaxJoins())); + } else if (sVar == "notraffictimeout") { + unsigned int i = sValue.ToUInt(); + if (i < 30) { + PutModule("Timeout can't be less than 30 seconds!"); + } else { + pUser->SetNoTrafficTimeout(i); + PutModule("NoTrafficTimeout = " + + CString(pUser->GetNoTrafficTimeout())); + } } else if (sVar == "maxnetworks") { if (GetUser()->IsAdmin()) { unsigned int i = sValue.ToUInt(); diff --git a/modules/data/webadmin/tmpl/add_edit_user.tmpl b/modules/data/webadmin/tmpl/add_edit_user.tmpl index 204c37d9..d396363b 100644 --- a/modules/data/webadmin/tmpl/add_edit_user.tmpl +++ b/modules/data/webadmin/tmpl/add_edit_user.tmpl @@ -312,6 +312,11 @@ "/> +
+
+ "/> +
SetMaxQueryBuffers( WebSock.GetParam("maxquerybuffers").ToUInt()); + unsigned int uNoTrafficTimeout = + WebSock.GetParam("notraffictimeout").ToUInt(); + if (uNoTrafficTimeout < 30) { + uNoTrafficTimeout = 30; + WebSock.GetSession()->AddError( + t_s("Timeout can't be less than 30 seconds!")); + } + pNewUser->SetNoTrafficTimeout(uNoTrafficTimeout); #ifdef HAVE_I18N pNewUser->SetLanguage(WebSock.GetParam("language")); @@ -1319,6 +1327,8 @@ class CWebAdminMod : public CModule { Tmpl["TimestampFormat"] = pUser->GetTimestampFormat(); Tmpl["Timezone"] = pUser->GetTimezone(); Tmpl["JoinTries"] = CString(pUser->JoinTries()); + Tmpl["NoTrafficTimeout"] = + CString(pUser->GetNoTrafficTimeout()); Tmpl["MaxNetworks"] = CString(pUser->MaxNetworks()); Tmpl["MaxJoins"] = CString(pUser->MaxJoins()); Tmpl["MaxQueryBuffers"] = CString(pUser->MaxQueryBuffers()); diff --git a/src/Client.cpp b/src/Client.cpp index 2da282d9..2339b017 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -388,7 +388,7 @@ void CClient::AcceptLogin(CUser& User) { // Set our proper timeout and set back our proper timeout mode // (constructor set a different timeout and mode) - SetTimeout(CIRCNetwork::NO_TRAFFIC_TIMEOUT, TMO_READ); + SetTimeout(User.GetNoTrafficTimeout(), TMO_READ); SetSockName("USR::" + m_pUser->GetUserName()); SetEncoding(m_pUser->GetClientEncoding()); diff --git a/src/IRCNetwork.cpp b/src/IRCNetwork.cpp index 46390e80..cf86543b 100644 --- a/src/IRCNetwork.cpp +++ b/src/IRCNetwork.cpp @@ -36,7 +36,7 @@ class CIRCNetworkPingTimer : public CCron { SetName("CIRCNetworkPingTimer::" + m_pNetwork->GetUser()->GetUserName() + "::" + m_pNetwork->GetName()); - Start(CIRCNetwork::PING_SLACK); + Start(m_pNetwork->GetUser()->GetPingSlack()); } ~CIRCNetworkPingTimer() override {} @@ -47,20 +47,23 @@ class CIRCNetworkPingTimer : public CCron { protected: void RunJob() override { CIRCSock* pIRCSock = m_pNetwork->GetIRCSock(); + auto uFrequency = m_pNetwork->GetUser()->GetPingFrequency(); if (pIRCSock && - pIRCSock->GetTimeSinceLastDataTransaction() >= - CIRCNetwork::PING_FREQUENCY) { + pIRCSock->GetTimeSinceLastDataTransaction() >= uFrequency) { pIRCSock->PutIRC("PING :ZNC"); } const vector& vClients = m_pNetwork->GetClients(); for (CClient* pClient : vClients) { - if (pClient->GetTimeSinceLastDataTransaction() >= - CIRCNetwork::PING_FREQUENCY) { + if (pClient->GetTimeSinceLastDataTransaction() >= uFrequency) { pClient->PutClient("PING :ZNC"); } } + + // Restart timer for the case if the period had changed. Usually this is + // noop + Start(m_pNetwork->GetUser()->GetPingSlack()); } private: @@ -68,13 +71,15 @@ class CIRCNetworkPingTimer : public CCron { }; class CIRCNetworkJoinTimer : public CCron { + constexpr static int JOIN_FREQUENCY = 30 /* seconds */; + public: CIRCNetworkJoinTimer(CIRCNetwork* pNetwork) : CCron(), m_bDelayed(false), m_pNetwork(pNetwork) { SetName("CIRCNetworkJoinTimer::" + m_pNetwork->GetUser()->GetUserName() + "::" + m_pNetwork->GetName()); - Start(CIRCNetwork::JOIN_FREQUENCY); + Start(JOIN_FREQUENCY); } ~CIRCNetworkJoinTimer() override {} @@ -91,7 +96,7 @@ class CIRCNetworkJoinTimer : public CCron { void RunJob() override { if (m_bDelayed) { m_bDelayed = false; - Start(CIRCNetwork::JOIN_FREQUENCY); + Start(JOIN_FREQUENCY); } if (m_pNetwork->IsIRCConnected()) { m_pNetwork->JoinChans(); diff --git a/src/IRCSock.cpp b/src/IRCSock.cpp index b32b4bfc..166caa23 100644 --- a/src/IRCSock.cpp +++ b/src/IRCSock.cpp @@ -686,7 +686,7 @@ bool CIRCSock::OnNumericMessage(CNumericMessage& Message) { m_pNetwork->SetIRCServer(sServer); // Now that we are connected, let nature take its course - SetTimeout(CIRCNetwork::NO_TRAFFIC_TIMEOUT, TMO_READ); + SetTimeout(m_pNetwork->GetUser()->GetNoTrafficTimeout(), TMO_READ); PutIRC("WHO " + sNick); m_bAuthed = true; diff --git a/src/User.cpp b/src/User.cpp index b3a20def..06f1f37e 100644 --- a/src/User.cpp +++ b/src/User.cpp @@ -32,7 +32,7 @@ class CUserTimer : public CCron { public: CUserTimer(CUser* pUser) : CCron(), m_pUser(pUser) { SetName("CUserTimer::" + m_pUser->GetUserName()); - Start(CIRCNetwork::PING_SLACK); + Start(m_pUser->GetPingSlack()); } ~CUserTimer() override {} @@ -46,10 +46,14 @@ class CUserTimer : public CCron { for (CClient* pUserClient : vUserClients) { if (pUserClient->GetTimeSinceLastDataTransaction() >= - CIRCNetwork::PING_FREQUENCY) { + m_pUser->GetPingFrequency()) { pUserClient->PutClient("PING :ZNC"); } } + + // Restart timer for the case if the period had changed. Usually this is + // noop + Start(m_pUser->GetPingSlack()); } CUser* m_pUser; @@ -96,6 +100,7 @@ CUser::CUser(const CString& sUserName) m_uMaxNetworks(1), m_uMaxQueryBuffers(50), m_uMaxJoins(0), + m_uNoTrafficTimeout(180), m_sSkinName(""), m_pModules(new CModules) { m_pUserTimer = new CUserTimer(this); @@ -151,6 +156,7 @@ bool CUser::ParseConfig(CConfig* pConfig, CString& sError) { {"maxnetworks", &CUser::SetMaxNetworks}, {"maxquerybuffers", &CUser::SetMaxQueryBuffers}, {"maxjoins", &CUser::SetMaxJoins}, + {"notraffictimeout", &CUser::SetNoTrafficTimeout}, }; TOption BoolOptions[] = { {"keepbuffer", @@ -752,6 +758,7 @@ bool CUser::Clone(const CUser& User, CString& sErrorRet, bool bCloneNetworks) { SetMaxNetworks(User.MaxNetworks()); SetMaxQueryBuffers(User.MaxQueryBuffers()); SetMaxJoins(User.MaxJoins()); + SetNoTrafficTimeout(User.GetNoTrafficTimeout()); SetClientEncoding(User.GetClientEncoding()); SetLanguage(User.GetLanguage()); @@ -962,7 +969,8 @@ CConfig CUser::ToConfig() const { config.AddKeyValuePair("MaxQueryBuffers", CString(m_uMaxQueryBuffers)); config.AddKeyValuePair("MaxJoins", CString(m_uMaxJoins)); config.AddKeyValuePair("ClientEncoding", GetClientEncoding()); - config.AddKeyValuePair("Language", GetLanguage()); + config.AddKeyValuePair("Language", GetLanguage()); + config.AddKeyValuePair("NoTrafficTimeout", CString(GetNoTrafficTimeout())); // Allow Hosts if (!m_ssAllowedHosts.empty()) {