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