From a06b6495e882be9f41d0f28138defbc603493486 Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Sun, 9 Jun 2013 22:57:49 +0400 Subject: [PATCH] Revert "Rewrite the JOIN channel logic, dropping MaxJoins" This reverts commit db7c47f97d4b18bce7264af5db6331fc296ff69a. Too many joined channels at once started to cause disconnect because of "Max SendQ Exceeded", which is not much better than previous Flood. Now MaxJoins is 0 by default, which preserves the current behavior of joining all channels at once. If someone experiences those disconnects due to SendQ, they can tune MaxJoins. Fix #329 Conflicts: include/znc/User.h modules/controlpanel.cpp modules/webadmin.cpp src/User.cpp --- include/znc/IRCNetwork.h | 1 + include/znc/User.h | 3 + modules/controlpanel.cpp | 8 ++ modules/data/webadmin/tmpl/add_edit_user.tmpl | 5 ++ modules/webadmin.cpp | 2 + src/IRCNetwork.cpp | 81 ++++++++++++------- src/User.cpp | 7 +- 7 files changed, 74 insertions(+), 33 deletions(-) diff --git a/include/znc/IRCNetwork.h b/include/znc/IRCNetwork.h index 3fc4d56e..13c9ea2c 100644 --- a/include/znc/IRCNetwork.h +++ b/include/znc/IRCNetwork.h @@ -72,6 +72,7 @@ public: bool AddChan(const CString& sName, bool bInConfig); bool DelChan(const CString& sName); void JoinChans(); + void JoinChans(std::set& sChans); const CString& GetChanPrefixes() const { return m_sChanPrefixes; }; void SetChanPrefixes(const CString& s) { m_sChanPrefixes = s; }; diff --git a/include/znc/User.h b/include/znc/User.h index 0a630909..9906fefd 100644 --- a/include/znc/User.h +++ b/include/znc/User.h @@ -124,6 +124,7 @@ public: void SetTimestampPrepend(bool b) { m_bPrependTimestamp = b; } void SetTimezone(const CString& s) { m_sTimezone = s; } void SetJoinTries(unsigned int i) { m_uMaxJoinTries = i; } + void SetMaxJoins(unsigned int i) { m_uMaxJoins = i; } void SetSkinName(const CString& s) { m_sSkinName = s; } void SetMaxNetworks(unsigned int i) { m_uMaxNetworks = i; } // !Setters @@ -165,6 +166,7 @@ public: unsigned long long BytesRead() const { return m_uBytesRead; } unsigned long long BytesWritten() const { return m_uBytesWritten; } unsigned int JoinTries() const { return m_uMaxJoinTries; } + unsigned int MaxJoins() const { return m_uMaxJoins; } CString GetSkinName() const; unsigned int MaxNetworks() const { return m_uMaxNetworks; } // !Getters @@ -212,6 +214,7 @@ protected: unsigned long long m_uBytesWritten; unsigned int m_uMaxJoinTries; unsigned int m_uMaxNetworks; + unsigned int m_uMaxJoins; CString m_sSkinName; CModules* m_pModules; diff --git a/modules/controlpanel.cpp b/modules/controlpanel.cpp index 3933b11c..854c8125 100644 --- a/modules/controlpanel.cpp +++ b/modules/controlpanel.cpp @@ -59,6 +59,7 @@ class CAdminMod : public CModule { {"AutoClearChanBuffer", boolean}, {"Password", str}, {"JoinTries", integer}, + {"MaxJoins", integer}, {"Timezone", str}, {"Admin", boolean}, {"AppendTimestamp", boolean}, @@ -177,6 +178,8 @@ class CAdminMod : public CModule { PutModule("KeepBuffer = " + CString(!pUser->AutoClearChanBuffer())); // XXX compatibility crap, added in 0.207 else if (sVar == "autoclearchanbuffer") PutModule("AutoClearChanBuffer = " + CString(pUser->AutoClearChanBuffer())); + else if (sVar == "maxjoins") + PutModule("MaxJoins = " + CString(pUser->MaxJoins())); else if (sVar == "jointries") PutModule("JoinTries = " + CString(pUser->JoinTries())); else if (sVar == "timezone") @@ -292,6 +295,11 @@ class CAdminMod : public CModule { pUser->SetPass(sHash, CUser::HASH_DEFAULT, sSalt); PutModule("Password has been changed!"); } + else if (sVar == "maxjoins") { + unsigned int i = sValue.ToUInt(); + pUser->SetMaxJoins(i); + PutModule("MaxJoins = " + CString(pUser->MaxJoins())); + } else if (sVar == "jointries") { unsigned int i = sValue.ToUInt(); pUser->SetJoinTries(i); diff --git a/modules/data/webadmin/tmpl/add_edit_user.tmpl b/modules/data/webadmin/tmpl/add_edit_user.tmpl index d15d6171..894561b2 100644 --- a/modules/data/webadmin/tmpl/add_edit_user.tmpl +++ b/modules/data/webadmin/tmpl/add_edit_user.tmpl @@ -261,6 +261,11 @@ +
+
Max Joins:
+ +
Max IRC Networks Number:
SetTimestampPrepend(WebSock.GetParam("prependtimestamp").ToBool()); pNewUser->SetTimezone(WebSock.GetParam("timezone")); pNewUser->SetJoinTries(WebSock.GetParam("jointries").ToUInt()); + pNewUser->SetMaxJoins(WebSock.GetParam("maxjoins").ToUInt()); if (spSession->IsAdmin()) { pNewUser->SetDenyLoadMod(WebSock.GetParam("denyloadmod").ToBool()); @@ -1073,6 +1074,7 @@ public: Tmpl["Timezone"] = pUser->GetTimezone(); Tmpl["JoinTries"] = CString(pUser->JoinTries()); Tmpl["MaxNetworks"] = CString(pUser->MaxNetworks()); + Tmpl["MaxJoins"] = CString(pUser->MaxJoins()); const set& ssAllowedHosts = pUser->GetAllowedHosts(); for (set::const_iterator it = ssAllowedHosts.begin(); it != ssAllowedHosts.end(); ++it) { diff --git a/src/IRCNetwork.cpp b/src/IRCNetwork.cpp index 334b2cda..e0d7428a 100644 --- a/src/IRCNetwork.cpp +++ b/src/IRCNetwork.cpp @@ -668,46 +668,67 @@ bool CIRCNetwork::DelChan(const CString& sName) { } void CIRCNetwork::JoinChans() { + // Avoid divsion by zero, it's bad! + if (m_vChans.empty()) + return; + + // We start at a random offset into the channel list so that if your + // first 3 channels are invite-only and you got MaxJoins == 3, ZNC will + // still be able to join the rest of your channels. + unsigned int start = rand() % m_vChans.size(); + unsigned int uJoins = m_pUser->MaxJoins(); + set sChans; + for (unsigned int a = 0; a < m_vChans.size(); a++) { + unsigned int idx = (start + a) % m_vChans.size(); + CChan* pChan = m_vChans[idx]; + if (!pChan->IsOn() && !pChan->IsDisabled()) { + if (!JoinChan(pChan)) + continue; + + sChans.insert(pChan); + + // Limit the number of joins + if (uJoins != 0 && --uJoins == 0) + break; + } + } + + while (!sChans.empty()) + JoinChans(sChans); +} + +void CIRCNetwork::JoinChans(set& sChans) { + CString sKeys, sJoin; bool bHaveKey = false; - size_t joinLength = 4; // join - CString sChannels, sKeys; + size_t uiJoinLength = strlen("JOIN "); - for (vector::iterator it = m_vChans.begin(); it != m_vChans.end(); ++it) { - CChan *pChan = *it; + while (!sChans.empty()) { + set::iterator it = sChans.begin(); + const CString& sName = (*it)->GetName(); + const CString& sKey = (*it)->GetKey(); + size_t len = sName.length() + sKey.length(); + len += 2; // two comma - if (pChan->IsOn() || pChan->IsDisabled() || !JoinChan(pChan)) { - continue; - } + if (!sKeys.empty() && uiJoinLength + len >= 512) + break; - size_t length = pChan->GetName().length() + pChan->GetKey().length() + 2; // +2 for either space or commas - - if ((joinLength + length) >= 510) { - // Sent what we got, and cleanup - PutIRC("JOIN " + sChannels + (bHaveKey ? (" " + sKeys) : "")); - - sChannels = ""; - sKeys = ""; - joinLength = 4; // join - bHaveKey = false; - } - - if (!sChannels.empty()) { - sChannels += ","; + if (!sJoin.empty()) { + sJoin += ","; sKeys += ","; } - - if (!pChan->GetKey().empty()) { + uiJoinLength += len; + sJoin += sName; + if (!sKey.empty()) { + sKeys += sKey; bHaveKey = true; - sKeys += pChan->GetKey(); } - - sChannels += pChan->GetName(); - joinLength += length; + sChans.erase(it); } - if (!sChannels.empty()) { - PutIRC("JOIN " + sChannels + (bHaveKey ? (" " + sKeys) : "")); - } + if (bHaveKey) + PutIRC("JOIN " + sJoin + " " + sKeys); + else + PutIRC("JOIN " + sJoin); } bool CIRCNetwork::JoinChan(CChan* pChan) { diff --git a/src/User.cpp b/src/User.cpp index 763288d1..ed8160f5 100644 --- a/src/User.cpp +++ b/src/User.cpp @@ -87,6 +87,7 @@ CUser::CUser(const CString& sUserName) m_uBufferCount = 50; m_uMaxJoinTries = 10; m_bAutoClearChanBuffer = true; + m_uMaxJoins = 0; m_bBeingDeleted = false; m_sTimestampFormat = "[%H:%M:%S]"; m_bAppendTimestamp = false; @@ -144,6 +145,7 @@ bool CUser::ParseConfig(CConfig* pConfig, CString& sError) { TOption UIntOptions[] = { { "jointries", &CUser::SetJoinTries }, { "maxnetworks", &CUser::SetMaxNetworks }, + { "maxjoins", &CUser::SetMaxJoins }, }; size_t numUIntOptions = sizeof(UIntOptions) / sizeof(UIntOptions[0]); TOption BoolOptions[] = { @@ -189,9 +191,6 @@ bool CUser::ParseConfig(CConfig* pConfig, CString& sError) { CString sValue; - // MaxJoins has been removed - pConfig->FindStringEntry("maxjoins", sValue); - CString sDCCLookupValue; pConfig->FindStringEntry("dcclookupmethod", sDCCLookupValue); if (pConfig->FindStringEntry("bouncedccs", sValue)) { @@ -668,6 +667,7 @@ bool CUser::Clone(const CUser& User, CString& sErrorRet, bool bCloneNetworks) { SetBufferCount(User.GetBufferCount(), true); SetJoinTries(User.JoinTries()); SetMaxNetworks(User.MaxNetworks()); + SetMaxJoins(User.MaxJoins()); // Allowed Hosts m_ssAllowedHosts.clear(); @@ -863,6 +863,7 @@ CConfig CUser::ToConfig() { config.AddKeyValuePair("Timezone", m_sTimezone); config.AddKeyValuePair("JoinTries", CString(m_uMaxJoinTries)); config.AddKeyValuePair("MaxNetworks", CString(m_uMaxNetworks)); + config.AddKeyValuePair("MaxJoins", CString(m_uMaxJoins)); // Allow Hosts if (!m_ssAllowedHosts.empty()) {