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()) {